← Back to team overview

cloud-init-dev team mailing list archive

[Merge] ~smoser/cloud-init:bug/1692028-empty-macs-for-bymac into cloud-init:master

 

Scott Moser has proposed merging ~smoser/cloud-init:bug/1692028-empty-macs-for-bymac into cloud-init:master.

Commit message:
Fix get_interfaces_by_mac for empty macs

Some interfaces (greptap0 in the bug) have a mac address of
'00:00:00:00:00:00'.  That was causing a duplicate mac detection
as the 'lo' device also has that mac.

The change here is to just ignore macs other than 'lo' that have that.

LP: #1692028

Requested reviews:
  cloud-init commiters (cloud-init-dev)
Related bugs:
  Bug #1692028 in cloud-init: "duplicate mac address during config-drive configuration with LXD container on openstack"
  https://bugs.launchpad.net/cloud-init/+bug/1692028

For more details, see:
https://code.launchpad.net/~smoser/cloud-init/+git/cloud-init/+merge/324332
-- 
Your team cloud-init commiters is requested to review the proposed merge of ~smoser/cloud-init:bug/1692028-empty-macs-for-bymac into cloud-init:master.
diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py
index a072a8d..8c6cd05 100644
--- a/cloudinit/net/__init__.py
+++ b/cloudinit/net/__init__.py
@@ -393,6 +393,7 @@ def get_interfaces_by_mac():
         else:
             raise
     ret = {}
+    empty_mac = '00:00:00:00:00:00'
     for name in devs:
         if not interface_has_own_mac(name):
             continue
@@ -404,6 +405,8 @@ def get_interfaces_by_mac():
         # some devices may not have a mac (tun0)
         if not mac:
             continue
+        if mac == empty_mac and name != 'lo':
+            continue
         if mac in ret:
             raise RuntimeError(
                 "duplicate mac found! both '%s' and '%s' have mac '%s'" %
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
index d36d0e7..0ac3c56 100644
--- a/tests/unittests/test_net.py
+++ b/tests/unittests/test_net.py
@@ -1539,24 +1539,24 @@ class TestNetRenderers(CiTestCase):
 
 
 class TestGetInterfacesByMac(CiTestCase):
-    _data = {'devices': ['enp0s1', 'enp0s2', 'bond1', 'bridge1',
-                         'bridge1-nic', 'tun0', 'bond1.101'],
-             'bonds': ['bond1'],
+    _data = {'bonds': ['bond1'],
              'bridges': ['bridge1'],
              'vlans': ['bond1.101'],
              'own_macs': ['enp0s1', 'enp0s2', 'bridge1-nic', 'bridge1',
-                          'bond1.101'],
+                          'bond1.101', 'lo'],
              'macs': {'enp0s1': 'aa:aa:aa:aa:aa:01',
                       'enp0s2': 'aa:aa:aa:aa:aa:02',
                       'bond1': 'aa:aa:aa:aa:aa:01',
                       'bond1.101': 'aa:aa:aa:aa:aa:01',
                       'bridge1': 'aa:aa:aa:aa:aa:03',
                       'bridge1-nic': 'aa:aa:aa:aa:aa:03',
+                      'lo': '00:00:00:00:00:00',
+                      'greptap0': '00:00:00:00:00:00',
                       'tun0': None}}
     data = {}
 
     def _se_get_devicelist(self):
-        return self.data['devices']
+        return list(self.data['devices'])
 
     def _se_get_interface_mac(self, name):
         return self.data['macs'][name]
@@ -1572,6 +1572,7 @@ class TestGetInterfacesByMac(CiTestCase):
 
     def _mock_setup(self):
         self.data = copy.deepcopy(self._data)
+        self.data['devices'] = set(list(self.data['macs'].keys()))
         mocks = ('get_devicelist', 'get_interface_mac', 'is_bridge',
                  'interface_has_own_mac', 'is_vlan')
         self.mocks = {}
@@ -1586,6 +1587,11 @@ class TestGetInterfacesByMac(CiTestCase):
         self.data['macs']['bridge1-nic'] = self.data['macs']['enp0s1']
         self.assertRaises(RuntimeError, net.get_interfaces_by_mac)
 
+    def test_ignores_zero_macs(self):
+        self._mock_setup()
+        self.data['macs']['bridge1-nic'] = self.data['macs']['enp0s1']
+        self.assertRaises(RuntimeError, net.get_interfaces_by_mac)
+
     def test_excludes_any_without_mac_address(self):
         self._mock_setup()
         ret = net.get_interfaces_by_mac()
@@ -1599,7 +1605,7 @@ class TestGetInterfacesByMac(CiTestCase):
             [mock.call('enp0s1'), mock.call('bond1')], any_order=True)
         self.assertEqual(
             {'aa:aa:aa:aa:aa:01': 'enp0s1', 'aa:aa:aa:aa:aa:02': 'enp0s2',
-             'aa:aa:aa:aa:aa:03': 'bridge1-nic'},
+             'aa:aa:aa:aa:aa:03': 'bridge1-nic', '00:00:00:00:00:00': 'lo'},
             ret)
 
     def test_excludes_bridges(self):
@@ -1608,7 +1614,7 @@ class TestGetInterfacesByMac(CiTestCase):
         # set everything other than 'b1' to be a bridge.
         # then expect b1 is the only thing left.
         self.data['macs']['b1'] = 'aa:aa:aa:aa:aa:b1'
-        self.data['devices'].append('b1')
+        self.data['devices'].add('b1')
         self.data['bonds'] = []
         self.data['own_macs'] = self.data['devices']
         self.data['bridges'] = [f for f in self.data['devices'] if f != "b1"]
@@ -1625,7 +1631,7 @@ class TestGetInterfacesByMac(CiTestCase):
         # set everything other than 'b1' to be a vlan.
         # then expect b1 is the only thing left.
         self.data['macs']['b1'] = 'aa:aa:aa:aa:aa:b1'
-        self.data['devices'].append('b1')
+        self.data['devices'].add('b1')
         self.data['bonds'] = []
         self.data['bridges'] = []
         self.data['own_macs'] = self.data['devices']
@@ -1637,6 +1643,17 @@ class TestGetInterfacesByMac(CiTestCase):
              mock.call('b1')],
             any_order=True)
 
+    def test_duplicates_of_empty_mac_are_ok(self):
+        """Duplicate macs of 00:00:00:00:00:00 should be skipped."""
+        self._mock_setup()
+        empty_mac = "00:00:00:00:00:00"
+        addnics = ('greptap1', 'lo', 'greptap2')
+        self.data['macs'].update(dict((k, empty_mac) for k in addnics))
+        self.data['devices'].update(set(addnics))
+        ret = net.get_interfaces_by_mac()
+        raise Exception(ret)
+        self.assertEqual('lo', ret[empty_mac])
+
 
 def _gzip_data(data):
     with io.BytesIO() as iobuf:

References