Blob Blame History Raw
From 02924179d423c919d0d46e6149da5bb8d26dd0d5 Mon Sep 17 00:00:00 2001
From: Eduardo Otubo <otubo@redhat.com>
Date: Tue, 3 Nov 2020 12:16:37 +0100
Subject: [PATCH 4/5] Explicit set IPV6_AUTOCONF and IPV6_FORCE_ACCEPT_RA on
 static6 (#634)

RH-Author: Eduardo Terrell Ferrari Otubo (eterrell)
RH-MergeRequest: 17: Explicit set IPV6_AUTOCONF and IPV6_FORCE_ACCEPT_RA on static6 (#634)
RH-Commit: [1/2] ba604c675f7c54a3e1768945a9ba77918ca4a57b (eterrell/cloud-init)
RH-Bugzilla: 1894015

commit b46e4a8cff667c8441622089cf7d57aeb88220cd
Author: Eduardo Otubo <otubo@redhat.com>
Date:   Thu Oct 29 15:05:42 2020 +0100

    Explicit set IPV6_AUTOCONF and IPV6_FORCE_ACCEPT_RA on static6 (#634)

    The static and static6 subnet types for network_data.json were
    being ignored by the Openstack handler, this would cause the code to
    break and not function properly.

    As of today, if a static6 configuration is chosen, the interface will
    still eventually be available to receive router advertisements or be set
    from NetworkManager to wait for them and cycle the interface in negative
    case.

    It is safe to assume that if the interface is manually configured to use
    static ipv6 address, there's no need to wait for router advertisements.
    This patch will set automatically IPV6_AUTOCONF and IPV6_FORCE_ACCEPT_RA
    both to "no" in this case.

    This patch fixes the specific behavior only for RHEL flavor and
    sysconfig renderer. It also introduces new unit tests for the specific
    case as well as adjusts some existent tests to be compatible with the
    new options. This patch also addresses this problem by assigning the
    appropriate subnet type for each case on the openstack handler.

    rhbz: #1889635
    rhbz: #1889635

    Signed-off-by: Eduardo Otubo otubo@redhat.com

Conflicts:
* The context of the patches are slightly different from upstream since
the there is more code added around the changes. But nothing interfering
on the patches.
* One minor conflict, removed the "flavor == 'rhel'" check because the
commit that introduced this change is after the 19.4 release. No harm
done since this commit is intended to be shipped to RHEL only anyways.

Signed-off-by: Eduardo Otubo <otubo@redhat.com>
---
 cloudinit/net/network_state.py                 |   3 +-
 cloudinit/net/sysconfig.py                     |   4 +
 cloudinit/sources/helpers/openstack.py         |   8 +-
 tests/unittests/test_distros/test_netconfig.py |   2 +
 tests/unittests/test_net.py                    | 100 +++++++++++++++++++++++++
 5 files changed, 115 insertions(+), 2 deletions(-)

diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py
index f3e8e25..2525fc9 100644
--- a/cloudinit/net/network_state.py
+++ b/cloudinit/net/network_state.py
@@ -822,7 +822,8 @@ def _normalize_subnet(subnet):
 
     if subnet.get('type') in ('static', 'static6'):
         normal_subnet.update(
-            _normalize_net_keys(normal_subnet, address_keys=('address',)))
+            _normalize_net_keys(normal_subnet, address_keys=(
+                'address', 'ip_address',)))
     normal_subnet['routes'] = [_normalize_route(r)
                                for r in subnet.get('routes', [])]
 
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
index 4b4ed09..4210544 100644
--- a/cloudinit/net/sysconfig.py
+++ b/cloudinit/net/sysconfig.py
@@ -401,6 +401,10 @@ class Renderer(renderer.Renderer):
                             ' because ipv4 subnet-level mtu:%s provided.',
                             iface_cfg.name, iface_cfg[mtu_key], subnet['mtu'])
                     iface_cfg[mtu_key] = subnet['mtu']
+
+                if subnet_is_ipv6(subnet):
+                    iface_cfg['IPV6_FORCE_ACCEPT_RA'] = False
+                    iface_cfg['IPV6_AUTOCONF'] = False
             elif subnet_type == 'manual':
                 # If the subnet has an MTU setting, then ONBOOT=True
                 # to apply the setting
diff --git a/cloudinit/sources/helpers/openstack.py b/cloudinit/sources/helpers/openstack.py
index 0778f45..6ef4f90 100644
--- a/cloudinit/sources/helpers/openstack.py
+++ b/cloudinit/sources/helpers/openstack.py
@@ -592,11 +592,17 @@ def convert_net_json(network_json=None, known_macs=None):
             elif network['type'] in ['ipv6_slaac', 'ipv6_dhcpv6-stateless',
                                      'ipv6_dhcpv6-stateful']:
                 subnet.update({'type': network['type']})
-            elif network['type'] in ['ipv4', 'ipv6']:
+            elif network['type'] in ['ipv4', 'static']:
                 subnet.update({
                     'type': 'static',
                     'address': network.get('ip_address'),
                 })
+            elif network['type'] in ['ipv6', 'static6']:
+                cfg.update({'accept-ra': False})
+                subnet.update({
+                    'type': 'static6',
+                    'address': network.get('ip_address'),
+                })
 
             # Enable accept_ra for stateful and legacy ipv6_dhcp types
             if network['type'] in ['ipv6_dhcpv6-stateful', 'ipv6_dhcp']:
diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py
index 4ea4203..b85a333 100644
--- a/tests/unittests/test_distros/test_netconfig.py
+++ b/tests/unittests/test_distros/test_netconfig.py
@@ -673,7 +673,9 @@ class TestNetCfgDistroOpensuse(TestNetCfgDistroBase):
                 IPADDR6=2607:f0d0:1002:0011::2/64
                 IPV6ADDR=2607:f0d0:1002:0011::2/64
                 IPV6INIT=yes
+                IPV6_AUTOCONF=no
                 IPV6_DEFAULTGW=2607:f0d0:1002:0011::1
+                IPV6_FORCE_ACCEPT_RA=no
                 NM_CONTROLLED=no
                 ONBOOT=yes
                 STARTMODE=auto
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index 2eedb12..b2b7c4b 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -768,7 +768,9 @@ IPADDR6_2=2001:DB10::10/64
 IPV6ADDR=2001:DB8::10/64
 IPV6ADDR_SECONDARIES="2001:DB9::10/64 2001:DB10::10/64"
 IPV6INIT=yes
+IPV6_AUTOCONF=no
 IPV6_DEFAULTGW=2001:DB8::1
+IPV6_FORCE_ACCEPT_RA=no
 NETMASK=255.255.252.0
 ONBOOT=yes
 STARTMODE=auto
@@ -1016,6 +1018,8 @@ NETWORK_CONFIGS = {
                 IPADDR6=2001:1::1/64
                 IPV6ADDR=2001:1::1/64
                 IPV6INIT=yes
+                IPV6_AUTOCONF=no
+                IPV6_FORCE_ACCEPT_RA=no
                 NETMASK=255.255.255.0
                 ONBOOT=yes
                 STARTMODE=auto
@@ -1201,6 +1205,33 @@ NETWORK_CONFIGS = {
             """),
         },
     },
+    'static6': {
+        'yaml': textwrap.dedent("""\
+        version: 1
+        config:
+          - type: 'physical'
+            name: 'iface0'
+            accept-ra: 'no'
+            subnets:
+            - type: 'static6'
+              address: 2001:1::1/64
+    """).rstrip(' '),
+        'expected_sysconfig_rhel': {
+            'ifcfg-iface0': textwrap.dedent("""\
+            BOOTPROTO=none
+            DEVICE=iface0
+            IPV6ADDR=2001:1::1/64
+            IPV6INIT=yes
+            IPV6_AUTOCONF=no
+            IPV6_FORCE_ACCEPT_RA=no
+            DEVICE=iface0
+            NM_CONTROLLED=no
+            ONBOOT=yes
+            TYPE=Ethernet
+            USERCTL=no
+            """),
+        },
+    },
     'dhcpv6_stateless': {
         'expected_eni': textwrap.dedent("""\
         auto lo
@@ -1507,6 +1538,8 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
                 IPADDR6=2001:1::1/64
                 IPV6ADDR=2001:1::1/64
                 IPV6INIT=yes
+                IPV6_AUTOCONF=no
+                IPV6_FORCE_ACCEPT_RA=no
                 IPV6_DEFAULTGW=2001:4800:78ff:1b::1
                 MACADDR=bb:bb:bb:bb:bb:aa
                 NETMASK=255.255.255.0
@@ -2067,6 +2100,8 @@ iface bond0 inet6 static
         IPADDR6=2001:1::1/92
         IPV6ADDR=2001:1::1/92
         IPV6INIT=yes
+        IPV6_AUTOCONF=no
+        IPV6_FORCE_ACCEPT_RA=no
         MTU=9000
         NETMASK=255.255.255.0
         NETMASK1=255.255.255.0
@@ -2154,6 +2189,8 @@ iface bond0 inet6 static
                 IPADDR6=2001:1::bbbb/96
                 IPV6ADDR=2001:1::bbbb/96
                 IPV6INIT=yes
+                IPV6_AUTOCONF=no
+                IPV6_FORCE_ACCEPT_RA=no
                 IPV6_DEFAULTGW=2001:1::1
                 MTU=2222
                 NETMASK=255.255.255.0
@@ -2213,6 +2250,9 @@ iface bond0 inet6 static
                 IPADDR6=2001:1::100/96
                 IPV6ADDR=2001:1::100/96
                 IPV6INIT=yes
+                IPV6_AUTOCONF=no
+                IPV6_FORCE_ACCEPT_RA=no
+                NM_CONTROLLED=no
                 ONBOOT=yes
                 STARTMODE=auto
                 TYPE=Ethernet
@@ -2226,6 +2266,9 @@ iface bond0 inet6 static
                 IPADDR6=2001:1::101/96
                 IPV6ADDR=2001:1::101/96
                 IPV6INIT=yes
+                IPV6_AUTOCONF=no
+                IPV6_FORCE_ACCEPT_RA=no
+                NM_CONTROLLED=no
                 ONBOOT=yes
                 STARTMODE=auto
                 TYPE=Ethernet
@@ -3015,6 +3058,61 @@ USERCTL=no
         self._compare_files_to_expected(entry[self.expected_name], found)
         self._assert_headers(found)
 
+    def test_stattic6_from_json(self):
+        net_json = {
+            "services": [{"type": "dns", "address": "172.19.0.12"}],
+            "networks": [{
+                "network_id": "dacd568d-5be6-4786-91fe-750c374b78b4",
+                "type": "ipv4", "netmask": "255.255.252.0",
+                "link": "tap1a81968a-79",
+                "routes": [{
+                    "netmask": "0.0.0.0",
+                    "network": "0.0.0.0",
+                    "gateway": "172.19.3.254",
+                }, {
+                    "netmask": "0.0.0.0",  # A second default gateway
+                    "network": "0.0.0.0",
+                    "gateway": "172.20.3.254",
+                }],
+                "ip_address": "172.19.1.34", "id": "network0"
+            }, {
+                "network_id": "mgmt",
+                "netmask": "ffff:ffff:ffff:ffff::",
+                "link": "interface1",
+                "mode": "link-local",
+                "routes": [],
+                "ip_address": "fe80::c096:67ff:fe5c:6e84",
+                "type": "static6",
+                "id": "network1",
+                "services": [],
+                "accept-ra": "false"
+            }],
+            "links": [
+                {
+                    "ethernet_mac_address": "fa:16:3e:ed:9a:59",
+                    "mtu": None, "type": "bridge", "id":
+                    "tap1a81968a-79",
+                    "vif_id": "1a81968a-797a-400f-8a80-567f997eb93f"
+                },
+            ],
+        }
+        macs = {'fa:16:3e:ed:9a:59': 'eth0'}
+        render_dir = self.tmp_dir()
+        network_cfg = openstack.convert_net_json(net_json, known_macs=macs)
+        ns = network_state.parse_net_config_data(network_cfg,
+                                                 skip_broken=False)
+        renderer = self._get_renderer()
+        with self.assertRaises(ValueError):
+            renderer.render_network_state(ns, target=render_dir)
+        self.assertEqual([], os.listdir(render_dir))
+
+    def test_static6_from_yaml(self):
+        entry = NETWORK_CONFIGS['static6']
+        found = self._render_and_read(network_config=yaml.load(
+            entry['yaml']))
+        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(
@@ -3133,6 +3231,8 @@ USERCTL=no
                    IPADDR6=2001:db8::100/32
                    IPV6ADDR=2001:db8::100/32
                    IPV6INIT=yes
+                   IPV6_AUTOCONF=no
+                   IPV6_FORCE_ACCEPT_RA=no
                    IPV6_DEFAULTGW=2001:db8::1
                    NETMASK=255.255.255.0
                    NM_CONTROLLED=no
-- 
1.8.3.1