ea00c4
From 51a90ecbdf1f3900183d8ec641eeb4571decf6dc Mon Sep 17 00:00:00 2001
ea00c4
From: Eduardo Otubo <otubo@redhat.com>
ea00c4
Date: Wed, 4 Nov 2020 12:37:54 +0100
ea00c4
Subject: [PATCH] network: Fix type and respect name when rendering vlan in
ea00c4
 sysconfig. (#541)
ea00c4
ea00c4
RH-Author: Eduardo Terrell Ferrari Otubo (eterrell)
ea00c4
RH-MergeRequest: 19: network: Fix type and respect name when rendering vlan in sysconfig. (#541)
ea00c4
RH-Commit: [1/1] 75bea46017397082c5763125a5f35806c2f840e9 (eterrell/cloud-init)
ea00c4
RH-Bugzilla: 1881462
ea00c4
ea00c4
commit 8439b191ec2f336d544cab86dba2860f969cd5b8
ea00c4
Author: Eduardo Otubo <otubo@redhat.com>
ea00c4
Date:   Tue Sep 15 18:00:00 2020 +0200
ea00c4
ea00c4
    network: Fix type and respect name when rendering vlan in sysconfig. (#541)
ea00c4
ea00c4
    Prior to this change, vlans were rendered in sysconfig with
ea00c4
    'TYPE=Ethernet', and incorrectly rendered the PHYSDEV based on
ea00c4
    the name of the vlan device rather than the 'link' provided
ea00c4
    in the network config.
ea00c4
ea00c4
    The change here fixes:
ea00c4
     * rendering of TYPE=Ethernet for a vlan
ea00c4
     * adds a warning if the configured device name is not supported
ea00c4
       per the RHEL 7 docs "11.5. Naming Scheme for VLAN Interfaces"
ea00c4
ea00c4
    LP: #1788915
ea00c4
    LP: #1826608
ea00c4
    RHBZ: #1861871
ea00c4
ea00c4
Signed-off-by: Eduardo Otubo <otubo@redhat.com>
ea00c4
---
ea00c4
 cloudinit/net/sysconfig.py                     | 32 +++++++++-
ea00c4
 tests/unittests/test_distros/test_netconfig.py | 81 ++++++++++++++++++++++++++
ea00c4
 tests/unittests/test_net.py                    |  4 --
ea00c4
 3 files changed, 112 insertions(+), 5 deletions(-)
ea00c4
ea00c4
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
ea00c4
index c078898..078636a 100644
ea00c4
--- a/cloudinit/net/sysconfig.py
ea00c4
+++ b/cloudinit/net/sysconfig.py
ea00c4
@@ -99,6 +99,10 @@ class ConfigMap(object):
ea00c4
     def __len__(self):
ea00c4
         return len(self._conf)
ea00c4
 
ea00c4
+    def skip_key_value(self, key, val):
ea00c4
+        """Skip the pair key, value if it matches a certain rule."""
ea00c4
+        return False
ea00c4
+
ea00c4
     def to_string(self):
ea00c4
         buf = io.StringIO()
ea00c4
         buf.write(_make_header())
ea00c4
@@ -106,6 +110,8 @@ class ConfigMap(object):
ea00c4
             buf.write("\n")
ea00c4
         for key in sorted(self._conf.keys()):
ea00c4
             value = self._conf[key]
ea00c4
+            if self.skip_key_value(key, value):
ea00c4
+                continue
ea00c4
             if isinstance(value, bool):
ea00c4
                 value = self._bool_map[value]
ea00c4
             if not isinstance(value, str):
ea00c4
@@ -214,6 +220,7 @@ class NetInterface(ConfigMap):
ea00c4
         'bond': 'Bond',
ea00c4
         'bridge': 'Bridge',
ea00c4
         'infiniband': 'InfiniBand',
ea00c4
+        'vlan': 'Vlan',
ea00c4
     }
ea00c4
 
ea00c4
     def __init__(self, iface_name, base_sysconf_dir, templates,
ea00c4
@@ -267,6 +274,11 @@ class NetInterface(ConfigMap):
ea00c4
             c.routes = self.routes.copy()
ea00c4
         return c
ea00c4
 
ea00c4
+    def skip_key_value(self, key, val):
ea00c4
+        if key == 'TYPE' and val == 'Vlan':
ea00c4
+            return True
ea00c4
+        return False
ea00c4
+
ea00c4
 
ea00c4
 class Renderer(renderer.Renderer):
ea00c4
     """Renders network information in a /etc/sysconfig format."""
ea00c4
@@ -701,7 +713,16 @@ class Renderer(renderer.Renderer):
ea00c4
                 iface_cfg['ETHERDEVICE'] = iface_name[:iface_name.rfind('.')]
ea00c4
             else:
ea00c4
                 iface_cfg['VLAN'] = True
ea00c4
-                iface_cfg['PHYSDEV'] = iface_name[:iface_name.rfind('.')]
ea00c4
+                iface_cfg.kind = 'vlan'
ea00c4
+
ea00c4
+                rdev = iface['vlan-raw-device']
ea00c4
+                supported = _supported_vlan_names(rdev, iface['vlan_id'])
ea00c4
+                if iface_name not in supported:
ea00c4
+                    LOG.info(
ea00c4
+                        "Name '%s' for vlan '%s' is not officially supported"
ea00c4
+                        "by RHEL. Supported: %s",
ea00c4
+                        iface_name, rdev, ' '.join(supported))
ea00c4
+                iface_cfg['PHYSDEV'] = rdev
ea00c4
 
ea00c4
             iface_subnets = iface.get("subnets", [])
ea00c4
             route_cfg = iface_cfg.routes
ea00c4
@@ -909,6 +930,15 @@ class Renderer(renderer.Renderer):
ea00c4
                             "\n".join(netcfg) + "\n", file_mode)
ea00c4
 
ea00c4
 
ea00c4
+def _supported_vlan_names(rdev, vid):
ea00c4
+    """Return list of supported names for vlan devices per RHEL doc
ea00c4
+    11.5. Naming Scheme for VLAN Interfaces."""
ea00c4
+    return [
ea00c4
+        v.format(rdev=rdev, vid=int(vid))
ea00c4
+        for v in ("{rdev}{vid:04}", "{rdev}{vid}",
ea00c4
+                  "{rdev}.{vid:04}", "{rdev}.{vid}")]
ea00c4
+
ea00c4
+
ea00c4
 def available(target=None):
ea00c4
     sysconfig = available_sysconfig(target=target)
ea00c4
     nm = available_nm(target=target)
ea00c4
diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py
ea00c4
index f9fc3a1..a1df066 100644
ea00c4
--- a/tests/unittests/test_distros/test_netconfig.py
ea00c4
+++ b/tests/unittests/test_distros/test_netconfig.py
ea00c4
@@ -541,6 +541,87 @@ class TestNetCfgDistroRedhat(TestNetCfgDistroBase):
ea00c4
                                V1_NET_CFG_IPV6,
ea00c4
                                expected_cfgs=expected_cfgs.copy())
ea00c4
 
ea00c4
+    def test_vlan_render_unsupported(self):
ea00c4
+        """Render officially unsupported vlan names."""
ea00c4
+        cfg = {
ea00c4
+            'version': 2,
ea00c4
+            'ethernets': {
ea00c4
+                'eth0': {'addresses': ["192.10.1.2/24"],
ea00c4
+                         'match': {'macaddress': "00:16:3e:60:7c:df"}}},
ea00c4
+            'vlans': {
ea00c4
+                'infra0': {'addresses': ["10.0.1.2/16"],
ea00c4
+                           'id': 1001, 'link': 'eth0'}},
ea00c4
+        }
ea00c4
+        expected_cfgs = {
ea00c4
+            self.ifcfg_path('eth0'): dedent("""\
ea00c4
+                BOOTPROTO=none
ea00c4
+                DEVICE=eth0
ea00c4
+                HWADDR=00:16:3e:60:7c:df
ea00c4
+                IPADDR=192.10.1.2
ea00c4
+                NETMASK=255.255.255.0
ea00c4
+                NM_CONTROLLED=no
ea00c4
+                ONBOOT=yes
ea00c4
+                TYPE=Ethernet
ea00c4
+                USERCTL=no
ea00c4
+                """),
ea00c4
+            self.ifcfg_path('infra0'): dedent("""\
ea00c4
+                BOOTPROTO=none
ea00c4
+                DEVICE=infra0
ea00c4
+                IPADDR=10.0.1.2
ea00c4
+                NETMASK=255.255.0.0
ea00c4
+                NM_CONTROLLED=no
ea00c4
+                ONBOOT=yes
ea00c4
+                PHYSDEV=eth0
ea00c4
+                USERCTL=no
ea00c4
+                VLAN=yes
ea00c4
+                """),
ea00c4
+            self.control_path(): dedent("""\
ea00c4
+                NETWORKING=yes
ea00c4
+                """),
ea00c4
+        }
ea00c4
+        self._apply_and_verify(
ea00c4
+            self.distro.apply_network_config, cfg,
ea00c4
+            expected_cfgs=expected_cfgs)
ea00c4
+
ea00c4
+    def test_vlan_render(self):
ea00c4
+        cfg = {
ea00c4
+            'version': 2,
ea00c4
+            'ethernets': {
ea00c4
+                'eth0': {'addresses': ["192.10.1.2/24"]}},
ea00c4
+            'vlans': {
ea00c4
+                'eth0.1001': {'addresses': ["10.0.1.2/16"],
ea00c4
+                              'id': 1001, 'link': 'eth0'}},
ea00c4
+        }
ea00c4
+        expected_cfgs = {
ea00c4
+            self.ifcfg_path('eth0'): dedent("""\
ea00c4
+                BOOTPROTO=none
ea00c4
+                DEVICE=eth0
ea00c4
+                IPADDR=192.10.1.2
ea00c4
+                NETMASK=255.255.255.0
ea00c4
+                NM_CONTROLLED=no
ea00c4
+                ONBOOT=yes
ea00c4
+                TYPE=Ethernet
ea00c4
+                USERCTL=no
ea00c4
+                """),
ea00c4
+            self.ifcfg_path('eth0.1001'): dedent("""\
ea00c4
+                BOOTPROTO=none
ea00c4
+                DEVICE=eth0.1001
ea00c4
+                IPADDR=10.0.1.2
ea00c4
+                NETMASK=255.255.0.0
ea00c4
+                NM_CONTROLLED=no
ea00c4
+                ONBOOT=yes
ea00c4
+                PHYSDEV=eth0
ea00c4
+                USERCTL=no
ea00c4
+                VLAN=yes
ea00c4
+                """),
ea00c4
+            self.control_path(): dedent("""\
ea00c4
+                NETWORKING=yes
ea00c4
+                """),
ea00c4
+        }
ea00c4
+        self._apply_and_verify(
ea00c4
+            self.distro.apply_network_config, cfg,
ea00c4
+            expected_cfgs=expected_cfgs)
ea00c4
+
ea00c4
 
ea00c4
 class TestNetCfgDistroOpensuse(TestNetCfgDistroBase):
ea00c4
 
ea00c4
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
ea00c4
index d7a7a65..c033745 100644
ea00c4
--- a/tests/unittests/test_net.py
ea00c4
+++ b/tests/unittests/test_net.py
ea00c4
@@ -1656,7 +1656,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
ea00c4
                 DHCLIENT_SET_DEFAULT_ROUTE=no
ea00c4
                 ONBOOT=yes
ea00c4
                 PHYSDEV=bond0
ea00c4
-                TYPE=Ethernet
ea00c4
                 USERCTL=no
ea00c4
                 VLAN=yes"""),
ea00c4
             'ifcfg-br0': textwrap.dedent("""\
ea00c4
@@ -1699,7 +1698,6 @@ pre-down route del -net 10.0.0.0/8 gw 11.0.0.1 metric 3 || true
ea00c4
                 NETMASK1=255.255.255.0
ea00c4
                 ONBOOT=yes
ea00c4
                 PHYSDEV=eth0
ea00c4
-                TYPE=Ethernet
ea00c4
                 USERCTL=no
ea00c4
                 VLAN=yes"""),
ea00c4
             'ifcfg-eth1': textwrap.dedent("""\
ea00c4
@@ -2302,7 +2300,6 @@ iface bond0 inet6 static
ea00c4
                 NETMASK1=255.255.255.0
ea00c4
                 ONBOOT=yes
ea00c4
                 PHYSDEV=en0
ea00c4
-                TYPE=Ethernet
ea00c4
                 USERCTL=no
ea00c4
                 VLAN=yes"""),
ea00c4
         },
ea00c4
@@ -3409,7 +3406,6 @@ USERCTL=no
ea00c4
                 NM_CONTROLLED=no
ea00c4
                 ONBOOT=yes
ea00c4
                 PHYSDEV=eno1
ea00c4
-                TYPE=Ethernet
ea00c4
                 USERCTL=no
ea00c4
                 VLAN=yes
ea00c4
                 """)
ea00c4
-- 
ea00c4
1.8.3.1
ea00c4