3956c3
From 8e8e1dffc528b738f92cd508d7a6faf58e40af00 Mon Sep 17 00:00:00 2001
7fc49f
From: Scott Moser <smoser@brickies.net>
7fc49f
Date: Wed, 15 Mar 2017 12:06:40 -0400
3956c3
Subject: [PATCH 1/2] support 'loopback' as a device type.
7fc49f
7fc49f
As reported in bug 1671927, sysconfig had an issue with rendering
7fc49f
a loopback device.  The problem was that some as yet unknown issue was
7fc49f
causing the openstack config drive to parse the provided ENI file rather
7fc49f
than reading the network_data.json.  Parsing an ENI file would add a
7fc49f
a 'lo' device of type 'physical', and sysconfig was failing to render
7fc49f
that.
7fc49f
7fc49f
The change here is:
7fc49f
 a.) add a 'loopback' type rather than 'physical' for network config.
7fc49f
     {'name': 'lo', 'type': 'loopback', 'subnets': ['type': 'loopback']}
7fc49f
 b.) support skipping that type in the eni and sysconfig renderers.
7fc49f
 c.) make network_state just piggy back on 'physical' renderer for
7fc49f
     loopback (this was what was happening before).
7fc49f
7fc49f
Tests are added for eni and sysconfig renderer.
7fc49f
7fc49f
(cherry picked from commit 1a2ca7530518d819cbab7287b12f942743427e38)
7fc49f
3956c3
Related: rhbz#1492726
3956c3
Signed-off-by: Ryan McCabe <rmccabe@redhat.com>
7fc49f
---
7fc49f
 cloudinit/net/eni.py           | 16 +++++++++------
7fc49f
 cloudinit/net/network_state.py |  4 ++++
7fc49f
 cloudinit/net/sysconfig.py     |  2 ++
7fc49f
 tests/unittests/test_net.py    | 44 ++++++++++++++++++++++++++++++++++++++++++
7fc49f
 4 files changed, 60 insertions(+), 6 deletions(-)
7fc49f
7fc49f
diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py
3956c3
index 5b249f1f..69ecbb5d 100644
7fc49f
--- a/cloudinit/net/eni.py
7fc49f
+++ b/cloudinit/net/eni.py
3956c3
@@ -273,8 +273,11 @@ def _ifaces_to_net_config_data(ifaces):
7fc49f
         # devname is 'eth0' for name='eth0:1'
7fc49f
         devname = name.partition(":")[0]
7fc49f
         if devname not in devs:
7fc49f
-            devs[devname] = {'type': 'physical', 'name': devname,
7fc49f
-                             'subnets': []}
7fc49f
+            if devname == "lo":
7fc49f
+                dtype = "loopback"
7fc49f
+            else:
7fc49f
+                dtype = "physical"
7fc49f
+            devs[devname] = {'type': dtype, 'name': devname, 'subnets': []}
7fc49f
             # this isnt strictly correct, but some might specify
7fc49f
             # hwaddress on a nic for matching / declaring name.
7fc49f
             if 'hwaddress' in data:
3956c3
@@ -423,10 +426,11 @@ class Renderer(renderer.Renderer):
7fc49f
             bonding
7fc49f
         '''
7fc49f
         order = {
7fc49f
-            'physical': 0,
7fc49f
-            'bond': 1,
7fc49f
-            'bridge': 2,
7fc49f
-            'vlan': 3,
7fc49f
+            'loopback': 0,
7fc49f
+            'physical': 1,
7fc49f
+            'bond': 2,
7fc49f
+            'bridge': 3,
7fc49f
+            'vlan': 4,
7fc49f
         }
7fc49f
 
7fc49f
         sections = []
7fc49f
diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py
7fc49f
index 11ef585b..90b2835a 100644
7fc49f
--- a/cloudinit/net/network_state.py
7fc49f
+++ b/cloudinit/net/network_state.py
3956c3
@@ -212,6 +212,10 @@ class NetworkStateInterpreter(object):
7fc49f
                     LOG.debug(self.dump_network_state())
7fc49f
 
3956c3
     @ensure_command_keys(['name'])
7fc49f
+    def handle_loopback(self, command):
7fc49f
+        return self.handle_physical(command)
7fc49f
+
3956c3
+    @ensure_command_keys(['name'])
7fc49f
     def handle_physical(self, command):
7fc49f
         '''
3956c3
         command = {
7fc49f
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
7fc49f
index efd101ca..25c29104 100644
7fc49f
--- a/cloudinit/net/sysconfig.py
7fc49f
+++ b/cloudinit/net/sysconfig.py
7fc49f
@@ -500,6 +500,8 @@ class Renderer(renderer.Renderer):
7fc49f
         '''Given state, return /etc/sysconfig files + contents'''
7fc49f
         iface_contents = {}
7fc49f
         for iface in network_state.iter_interfaces():
7fc49f
+            if iface['type'] == "loopback":
7fc49f
+                continue
7fc49f
             iface_name = iface['name']
7fc49f
             iface_cfg = NetInterface(iface_name, base_sysconf_dir)
7fc49f
             cls._render_iface_shared(iface, iface_cfg)
7fc49f
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
3956c3
index 4c0e3ad3..7e389c10 100644
7fc49f
--- a/tests/unittests/test_net.py
7fc49f
+++ b/tests/unittests/test_net.py
3956c3
@@ -615,6 +615,14 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
7fc49f
     }
7fc49f
 }
7fc49f
 
7fc49f
+CONFIG_V1_EXPLICIT_LOOPBACK = {
7fc49f
+    'version': 1,
7fc49f
+    'config': [{'name': 'eth0', 'type': 'physical',
7fc49f
+               'subnets': [{'control': 'auto', 'type': 'dhcp'}]},
7fc49f
+               {'name': 'lo', 'type': 'loopback',
7fc49f
+                'subnets': [{'control': 'auto', 'type': 'loopback'}]},
7fc49f
+               ]}
7fc49f
+
7fc49f
 
7fc49f
 def _setup_test(tmp_dir, mock_get_devicelist, mock_read_sys_net,
7fc49f
                 mock_sys_dev_path):
3956c3
@@ -785,6 +793,27 @@ USERCTL=no
7fc49f
                 with open(os.path.join(render_dir, fn)) as fh:
7fc49f
                     self.assertEqual(expected_content, fh.read())
7fc49f
 
7fc49f
+    def test_config_with_explicit_loopback(self):
7fc49f
+        ns = network_state.parse_net_config_data(CONFIG_V1_EXPLICIT_LOOPBACK)
7fc49f
+        render_dir = self.tmp_path("render")
7fc49f
+        os.makedirs(render_dir)
7fc49f
+        renderer = sysconfig.Renderer()
7fc49f
+        renderer.render_network_state(render_dir, ns)
7fc49f
+        found = dir2dict(render_dir)
7fc49f
+        nspath = '/etc/sysconfig/network-scripts/'
7fc49f
+        self.assertNotIn(nspath + 'ifcfg-lo', found.keys())
7fc49f
+        expected = """\
7fc49f
+# Created by cloud-init on instance boot automatically, do not edit.
7fc49f
+#
7fc49f
+BOOTPROTO=dhcp
7fc49f
+DEVICE=eth0
7fc49f
+NM_CONTROLLED=no
7fc49f
+ONBOOT=yes
7fc49f
+TYPE=Ethernet
7fc49f
+USERCTL=no
7fc49f
+"""
7fc49f
+        self.assertEqual(expected, found[nspath + 'ifcfg-eth0'])
7fc49f
+
7fc49f
 
7fc49f
 class TestEniNetRendering(TestCase):
7fc49f
 
3956c3
@@ -826,6 +855,21 @@ iface eth1000 inet dhcp
7fc49f
 """
7fc49f
         self.assertEqual(expected.lstrip(), contents.lstrip())
7fc49f
 
7fc49f
+    def test_config_with_explicit_loopback(self):
7fc49f
+        tmp_dir = self.tmp_dir()
7fc49f
+        ns = network_state.parse_net_config_data(CONFIG_V1_EXPLICIT_LOOPBACK)
7fc49f
+        renderer = eni.Renderer()
7fc49f
+        renderer.render_network_state(tmp_dir, ns)
7fc49f
+        expected = """\
7fc49f
+auto lo
7fc49f
+iface lo inet loopback
7fc49f
+
7fc49f
+auto eth0
7fc49f
+iface eth0 inet dhcp
7fc49f
+"""
7fc49f
+        self.assertEqual(
7fc49f
+            expected, dir2dict(tmp_dir)['/etc/network/interfaces'])
7fc49f
+
7fc49f
 
7fc49f
 class TestEniNetworkStateToEni(TestCase):
7fc49f
     mycfg = {
7fc49f
-- 
3956c3
2.13.6
7fc49f