Scott Moser has proposed merging 
~smoser/cloud-init:bug/1752391-fix-iscsi-root-without-ip into cloud-init:master.

Commit message:
net: recognize iscsi root cases without ip= on kernel command line.

When 'ip=' or 'ip6=' is found on the kernel command line,
cloud-init will consider read network config from /run/net-*.conf files.

There are some iscsi-root scenarios where initramfs configures networking
but the ip= parameter is not present. 2 such cases are:
 a.) static config in /etc/iscsi/iscsi.initramfs (copied into the initramfs)
 b.) iBft

This changes cloud-init to consider initramfs provided networking
information if:
 * there are /ruin/net-* files and
 * (ip= or ip6 is on the command line) or open-iscsi.interface file exists.

LP: #1752391

Requested reviews:
  cloud-init commiters (cloud-init-dev)
Related bugs:
  Bug #1752391 in cloud-init (Ubuntu): "cloud-init does not recognize initramfs 
provided network config in all cases"
  https://bugs.launchpad.net/ubuntu/+source/cloud-init/+bug/1752391

For more details, see:
https://code.launchpad.net/~smoser/cloud-init/+git/cloud-init/+merge/340140

see commit message
-- 
Your team cloud-init commiters is requested to review the proposed merge of 
~smoser/cloud-init:bug/1752391-fix-iscsi-root-without-ip into cloud-init:master.
diff --git a/cloudinit/net/cmdline.py b/cloudinit/net/cmdline.py
index 7b2cc9d..a123afb 100755
--- a/cloudinit/net/cmdline.py
+++ b/cloudinit/net/cmdline.py
@@ -9,12 +9,15 @@ import base64
 import glob
 import gzip
 import io
+import os
 
 from . import get_devicelist
 from . import read_sys_net_safe
 
 from cloudinit import util
 
+_OPEN_ISCSI_INTERFACE_FILE = "/run/initramfs/open-iscsi.interface"
+
 
 def _klibc_to_config_entry(content, mac_addrs=None):
     """Convert a klibc written shell content file to a 'config' entry
@@ -103,9 +106,13 @@ def _klibc_to_config_entry(content, mac_addrs=None):
     return name, iface
 
 
+def _get_klibc_net_cfg_files():
+    return glob.glob('/run/net-*.conf') + glob.glob('/run/net6-*.conf')
+
+
 def config_from_klibc_net_cfg(files=None, mac_addrs=None):
     if files is None:
-        files = glob.glob('/run/net-*.conf') + glob.glob('/run/net6-*.conf')
+        files = _get_klibc_net_cfg_files()
 
     entries = []
     names = {}
@@ -160,10 +167,23 @@ def _b64dgz(b64str, gzipped="try"):
     return _decomp_gzip(blob, strict=gzipped != "try")
 
 
+def _is_initramfs_netconfig(files, cmdline):
+    if files:
+        if 'ip=' in cmdline or 'ip6' in cmdline:
+            return True
+        if os.path.exists(_OPEN_ISCSI_INTERFACE_FILE):
+            # iBft can configure networking without ip=
+            return True
+    return False
+
+
 def read_kernel_cmdline_config(files=None, mac_addrs=None, cmdline=None):
     if cmdline is None:
         cmdline = util.get_cmdline()
 
+    if files is None:
+        files = _get_klibc_net_cfg_files()
+
     if 'network-config=' in cmdline:
         data64 = None
         for tok in cmdline.split():
@@ -172,7 +192,7 @@ def read_kernel_cmdline_config(files=None, mac_addrs=None, cmdline=None):
         if data64:
             return util.load_yaml(_b64dgz(data64))
 
-    if 'ip=' not in cmdline and 'ip6=' not in cmdline:
+    if not _is_initramfs_netconfig(files, cmdline):
         return None
 
     if mac_addrs is None:
diff --git a/cloudinit/tests/helpers.py b/cloudinit/tests/helpers.py
index 41d9a8e..03fd765 100644
--- a/cloudinit/tests/helpers.py
+++ b/cloudinit/tests/helpers.py
@@ -283,10 +283,15 @@ class FilesystemMockingTestCase(ResourceUsingTestCase):
     def patchOS(self, new_root):
         patch_funcs = {
             os.path: [('isfile', 1), ('exists', 1),
-                      ('islink', 1), ('isdir', 1)],
+                      ('islink', 1), ('isdir', 1), ('lexists', 1)],
             os: [('listdir', 1), ('mkdir', 1),
-                 ('lstat', 1), ('symlink', 2)],
+                 ('lstat', 1), ('symlink', 2)]
         }
+
+        if hasattr(os, 'scandir'):
+            # py27 does not have scandir
+            patch_funcs['os'].append(('scandir', 1))
+
         for (mod, funcs) in patch_funcs.items():
             for f, nargs in funcs:
                 func = getattr(mod, f)
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index ac33e8e..b7124e6 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -12,10 +12,8 @@ from cloudinit.sources.helpers import openstack
 from cloudinit import temp_utils
 from cloudinit import util
 
-from cloudinit.tests.helpers import CiTestCase
-from cloudinit.tests.helpers import dir2dict
-from cloudinit.tests.helpers import mock
-from cloudinit.tests.helpers import populate_dir
+from cloudinit.tests.helpers import (
+    CiTestCase, FilesystemMockingTestCase, dir2dict, mock, populate_dir)
 
 import base64
 import copy
@@ -2183,27 +2181,49 @@ class TestCmdlineConfigParsing(CiTestCase):
         self.assertEqual(found, self.simple_cfg)
 
 
-class TestCmdlineReadKernelConfig(CiTestCase):
+class TestCmdlineReadKernelConfig(FilesystemMockingTestCase):
     macs = {
         'eth0': '14:02:ec:42:48:00',
         'eno1': '14:02:ec:42:48:01',
     }
 
-    def test_ip_cmdline_read_kernel_cmdline_ip(self):
-        content = {'net-eth0.conf': DHCP_CONTENT_1}
-        files = sorted(populate_dir(self.tmp_dir(), content))
+    def test_ip_cmdline_without_ip(self):
+        content = {'/run/net-eth0.conf': DHCP_CONTENT_1,
+                   cmdline._OPEN_ISCSI_INTERFACE_FILE: "eth0\n"}
+        exp1 = copy.deepcopy(DHCP_EXPECTED_1)
+        exp1['mac_address'] = self.macs['eth0']
+
+        root = self.tmp_dir()
+        populate_dir(root, content)
+        self.reRoot(root)
+
         found = cmdline.read_kernel_cmdline_config(
-            files=files, cmdline='foo ip=dhcp', mac_addrs=self.macs)
+            cmdline='foo root=/root/bar', mac_addrs=self.macs)
+        self.assertEqual(found['version'], 1)
+        self.assertEqual(found['config'], [exp1])
+
+    def test_ip_cmdline_read_kernel_cmdline_ip(self):
+        content = {'/run/net-eth0.conf': DHCP_CONTENT_1}
         exp1 = copy.deepcopy(DHCP_EXPECTED_1)
         exp1['mac_address'] = self.macs['eth0']
+
+        root = self.tmp_dir()
+        populate_dir(root, content)
+        self.reRoot(root)
+
+        found = cmdline.read_kernel_cmdline_config(
+            cmdline='foo ip=dhcp', mac_addrs=self.macs)
         self.assertEqual(found['version'], 1)
         self.assertEqual(found['config'], [exp1])
 
     def test_ip_cmdline_read_kernel_cmdline_ip6(self):
-        content = {'net6-eno1.conf': DHCP6_CONTENT_1}
-        files = sorted(populate_dir(self.tmp_dir(), content))
+        content = {'/run/net6-eno1.conf': DHCP6_CONTENT_1}
+        root = self.tmp_dir()
+        populate_dir(root, content)
+        self.reRoot(root)
+
         found = cmdline.read_kernel_cmdline_config(
-            files=files, cmdline='foo ip6=dhcp root=/dev/sda',
+            cmdline='foo ip6=dhcp root=/dev/sda',
             mac_addrs=self.macs)
         self.assertEqual(
             found,
@@ -2223,18 +2243,23 @@ class TestCmdlineReadKernelConfig(CiTestCase):
         self.assertIsNone(found)
 
     def test_ip_cmdline_both_ip_ip6(self):
-        content = {'net-eth0.conf': DHCP_CONTENT_1,
-                   'net6-eth0.conf': DHCP6_CONTENT_1.replace('eno1', 'eth0')}
-        files = sorted(populate_dir(self.tmp_dir(), content))
-        found = cmdline.read_kernel_cmdline_config(
-            files=files, cmdline='foo ip=dhcp ip6=dhcp', mac_addrs=self.macs)
-
+        content = {
+            '/run/net-eth0.conf': DHCP_CONTENT_1,
+            '/run/net6-eth0.conf': DHCP6_CONTENT_1.replace('eno1', 'eth0')}
         eth0 = copy.deepcopy(DHCP_EXPECTED_1)
         eth0['mac_address'] = self.macs['eth0']
         eth0['subnets'].append(
             {'control': 'manual', 'type': 'dhcp6',
              'netmask': '64', 'dns_nameservers': ['2001:67c:1562:8010::2:1']})
         expected = [eth0]
+
+        root = self.tmp_dir()
+        populate_dir(root, content)
+        self.reRoot(root)
+
+        found = cmdline.read_kernel_cmdline_config(
+            cmdline='foo ip=dhcp ip6=dhcp', mac_addrs=self.macs)
+
         self.assertEqual(found['version'], 1)
         self.assertEqual(found['config'], expected)
 
_______________________________________________
Mailing list: https://launchpad.net/~cloud-init-dev
Post to     : cloud-init-dev@lists.launchpad.net
Unsubscribe : https://launchpad.net/~cloud-init-dev
More help   : https://help.launchpad.net/ListHelp

Reply via email to