diff --git a/cloudbaseinit/plugins/common/networkconfig.py b/cloudbaseinit/plugins/common/networkconfig.py index 30d7b2e0..3c16d6d4 100644 --- a/cloudbaseinit/plugins/common/networkconfig.py +++ b/cloudbaseinit/plugins/common/networkconfig.py @@ -13,6 +13,8 @@ # under the License. +import re + from cloudbaseinit import exception from cloudbaseinit.metadata.services import base as service_base from cloudbaseinit.openstack.common import log as logging @@ -34,18 +36,30 @@ NET_REQUIRE = { } +def _name2idx(name): + """Get the position of a network interface by its name.""" + match = re.search(r"eth(\d+)", name, re.I) + if not match: + raise exception.CloudbaseInitException( + "invalid NetworkDetails name {!r}" + .format(name) + ) + return int(match.group(1)) + + def _preprocess_nics(network_details, network_adapters): """Check NICs and fill missing data if possible.""" - # initial checks + # Initial checks. if not network_adapters: raise exception.CloudbaseInitException( "no network adapters available") # Sort VM adapters by name (assuming that those # from the context are in correct order). + # Do this for a better matching by order + # if hardware address is missing. network_adapters = sorted(network_adapters, key=lambda arg: arg[0]) - _network_details = [] # store here processed data - # check and update every NetworkDetails object - ind = 0 + _network_details = [] # store here processed interfaces + # Check and update every NetworkDetails object. total = len(network_adapters) for nic in network_details: if not isinstance(nic, service_base.NetworkDetails): @@ -53,7 +67,7 @@ def _preprocess_nics(network_details, network_adapters): "invalid NetworkDetails object {!r}" .format(type(nic)) ) - # check requirements + # Check requirements. final_status = True for fields, status in NET_REQUIRE.items(): if not status: @@ -68,14 +82,14 @@ def _preprocess_nics(network_details, network_adapters): # Complete hardware address if missing by selecting # the corresponding MAC in terms of naming, then ordering. if not nic.mac: - mac = None - # by name + # By name... macs = [adapter[1] for adapter in network_adapters if adapter[0] == nic.name] mac = macs[0] if macs else None - # or by order - if not mac and ind < total: - mac = network_adapters[ind][1] + # ...or by order. + idx = _name2idx(nic.name) + if not mac and idx < total: + mac = network_adapters[idx][1] nic = service_base.NetworkDetails( nic.name, mac, @@ -86,7 +100,6 @@ def _preprocess_nics(network_details, network_adapters): nic.dnsnameservers ) _network_details.append(nic) - ind += 1 return _network_details diff --git a/cloudbaseinit/tests/plugins/common/test_networkconfig.py b/cloudbaseinit/tests/plugins/common/test_networkconfig.py index 4bab1776..477b7d25 100644 --- a/cloudbaseinit/tests/plugins/common/test_networkconfig.py +++ b/cloudbaseinit/tests/plugins/common/test_networkconfig.py @@ -87,15 +87,13 @@ class TestNetworkConfigPlugin(unittest.TestCase): reboot = len(missed_adapters) != self._count self.assertEqual((plugin_base.PLUGIN_EXECUTION_DONE, reboot), ret) - def _setUp(self, same_names=True): + def _setUp(self, same_names=True, wrong_names=False, no_macs=False): # Generate fake pairs of NetworkDetails objects and # local ethernet network adapters. + iface_name = "Ethernet" if wrong_names else "eth" self._count = 3 - details_names = [ - "eth0", - "eth1", - "eth2" - ] + details_names = ["{}{}".format(iface_name, idx) + for idx in range(self._count)] if same_names: adapters_names = details_names[:] else: @@ -120,7 +118,7 @@ class TestNetworkConfigPlugin(unittest.TestCase): "192.168.255.255", "192.168.122.127", ] - gateway = [ + gateways = [ "192.168.122.1", "192.168.122.16", "192.168.122.32", @@ -136,11 +134,11 @@ class TestNetworkConfigPlugin(unittest.TestCase): adapter = (adapters_names[ind], macs[ind]) nic = service_base.NetworkDetails( details_names[ind], - macs[ind], + None if no_macs else macs[ind], addresses[ind], netmasks[ind], broadcasts[ind], - gateway[ind], + gateways[ind], dnsnses[ind].split() ) self._network_adapters.append(adapter) @@ -166,6 +164,10 @@ class TestNetworkConfigPlugin(unittest.TestCase): self._network_details.append([None] * 6) self._partial_test_execute(invalid_details=True) + def test_execute_invalid_network_details_name(self): + self._setUp(wrong_names=True, no_macs=True) + self._partial_test_execute(invalid_details=True) + def test_execute_single(self): for _ in range(self._count - 1): self._network_adapters.pop()