← Back to team overview

cloud-init-dev team mailing list archive

[Merge] ~smoser/cloud-init:bug/net-explicit-lo-type into cloud-init:master

 

Scott Moser has proposed merging ~smoser/cloud-init:bug/net-explicit-lo-type into cloud-init:master.

Commit message:
net: support 'loopback' as a device type.

As reported in bug 1671927, sysconfig had an issue with rendering
a loopback device.  The problem was that some as yet unknown issue was
causing the openstack config drive to parse the provided ENI file rather
than reading the network_data.json.  Parsing an ENI file would add a
a 'lo' device of type 'physical', and sysconfig was failing to render
that.

The change here is:
 a.) add a 'loopback' type rather than 'physical' for network config.
     {'name': 'lo', 'type': 'loopback', 'subnets': ['type': 'loopback']}
 b.) support skipping that type in the eni and sysconfig renderers.
 c.) make network_state just piggy back on 'physical' renderer for
     loopback (this was what was happening before).

Tests are added for eni and sysconfig renderer.

Requested reviews:
  cloud init development team (cloud-init-dev)

For more details, see:
https://code.launchpad.net/~smoser/cloud-init/+git/cloud-init/+merge/319943
-- 
Your team cloud init development team is requested to review the proposed merge of ~smoser/cloud-init:bug/net-explicit-lo-type into cloud-init:master.
diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py
index 5b249f1..69ecbb5 100644
--- a/cloudinit/net/eni.py
+++ b/cloudinit/net/eni.py
@@ -273,8 +273,11 @@ def _ifaces_to_net_config_data(ifaces):
         # devname is 'eth0' for name='eth0:1'
         devname = name.partition(":")[0]
         if devname not in devs:
-            devs[devname] = {'type': 'physical', 'name': devname,
-                             'subnets': []}
+            if devname == "lo":
+                dtype = "loopback"
+            else:
+                dtype = "physical"
+            devs[devname] = {'type': dtype, 'name': devname, 'subnets': []}
             # this isnt strictly correct, but some might specify
             # hwaddress on a nic for matching / declaring name.
             if 'hwaddress' in data:
@@ -423,10 +426,11 @@ class Renderer(renderer.Renderer):
             bonding
         '''
         order = {
-            'physical': 0,
-            'bond': 1,
-            'bridge': 2,
-            'vlan': 3,
+            'loopback': 0,
+            'physical': 1,
+            'bond': 2,
+            'bridge': 3,
+            'vlan': 4,
         }
 
         sections = []
diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py
index 11ef585..90b2835 100644
--- a/cloudinit/net/network_state.py
+++ b/cloudinit/net/network_state.py
@@ -212,6 +212,10 @@ class NetworkStateInterpreter(object):
                     LOG.debug(self.dump_network_state())
 
     @ensure_command_keys(['name'])
+    def handle_loopback(self, command):
+        return self.handle_physical(command)
+
+    @ensure_command_keys(['name'])
     def handle_physical(self, command):
         '''
         command = {
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
index 06de660..7f52db4 100644
--- a/cloudinit/net/sysconfig.py
+++ b/cloudinit/net/sysconfig.py
@@ -368,6 +368,8 @@ class Renderer(renderer.Renderer):
         '''Given state, return /etc/sysconfig files + contents'''
         iface_contents = {}
         for iface in network_state.iter_interfaces():
+            if iface['type'] == "loopback":
+                continue
             iface_name = iface['name']
             iface_cfg = NetInterface(iface_name, base_sysconf_dir)
             cls._render_iface_shared(iface, iface_cfg)
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index 8d25310..c6535e1 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -637,6 +637,14 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
     }
 }
 
+CONFIG_V1_EXPLICIT_LOOPBACK = {
+    'version': 1,
+    'config': [{'name': 'eth0', 'type': 'physical',
+               'subnets': [{'control': 'auto', 'type': 'dhcp'}]},
+               {'name': 'lo', 'type': 'loopback',
+                'subnets': [{'control': 'auto', 'type': 'loopback'}]},
+               ]}
+
 
 def _setup_test(tmp_dir, mock_get_devicelist, mock_read_sys_net,
                 mock_sys_dev_path):
@@ -722,6 +730,27 @@ USERCTL=no
                 with open(os.path.join(render_dir, fn)) as fh:
                     self.assertEqual(expected_content, fh.read())
 
+    def test_config_with_explicit_loopback(self):
+        ns = network_state.parse_net_config_data(CONFIG_V1_EXPLICIT_LOOPBACK)
+        render_dir = self.tmp_path("render")
+        os.makedirs(render_dir)
+        renderer = sysconfig.Renderer()
+        renderer.render_network_state(render_dir, ns)
+        found = dir2dict(render_dir)
+        nspath = '/etc/sysconfig/network-scripts/'
+        self.assertNotIn(nspath + 'ifcfg-lo', found.keys())
+        expected = """\
+# Created by cloud-init on instance boot automatically, do not edit.
+#
+BOOTPROTO=dhcp
+DEVICE=eth0
+NM_CONTROLLED=no
+ONBOOT=yes
+TYPE=Ethernet
+USERCTL=no
+"""
+        self.assertEqual(expected, found[nspath + 'ifcfg-eth0'])
+
 
 class TestEniNetRendering(CiTestCase):
 
@@ -762,6 +791,21 @@ iface eth1000 inet dhcp
 """
         self.assertEqual(expected.lstrip(), contents.lstrip())
 
+    def test_config_with_explicit_loopback(self):
+        tmp_dir = self.tmp_dir()
+        ns = network_state.parse_net_config_data(CONFIG_V1_EXPLICIT_LOOPBACK)
+        renderer = eni.Renderer()
+        renderer.render_network_state(tmp_dir, ns)
+        expected = """\
+auto lo
+iface lo inet loopback
+
+auto eth0
+iface eth0 inet dhcp
+"""
+        self.assertEqual(
+            expected, dir2dict(tmp_dir)['/etc/network/interfaces'])
+
 
 class TestEniNetworkStateToEni(CiTestCase):
     mycfg = {

References