Better static network configuration support

When matching metadata network interface details with
the actual existing instance network adapters, keep
a valid alignment between their names by using their indexes.
This will be used if and only if the hardware address
is missing and the trivial name matching will fail.

Change-Id: I32b1efece3eb0a43432315ac6516561ef6ccbe37
This commit is contained in:
Cosmin Poieana 2015-03-19 20:17:56 +02:00
parent 0ccea78de6
commit 9319d74115
2 changed files with 35 additions and 20 deletions

View File

@ -13,6 +13,8 @@
# under the License. # under the License.
import re
from cloudbaseinit import exception from cloudbaseinit import exception
from cloudbaseinit.metadata.services import base as service_base from cloudbaseinit.metadata.services import base as service_base
from cloudbaseinit.openstack.common import log as logging 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): def _preprocess_nics(network_details, network_adapters):
"""Check NICs and fill missing data if possible.""" """Check NICs and fill missing data if possible."""
# initial checks # Initial checks.
if not network_adapters: if not network_adapters:
raise exception.CloudbaseInitException( raise exception.CloudbaseInitException(
"no network adapters available") "no network adapters available")
# Sort VM adapters by name (assuming that those # Sort VM adapters by name (assuming that those
# from the context are in correct order). # 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_adapters = sorted(network_adapters, key=lambda arg: arg[0])
_network_details = [] # store here processed data _network_details = [] # store here processed interfaces
# check and update every NetworkDetails object # Check and update every NetworkDetails object.
ind = 0
total = len(network_adapters) total = len(network_adapters)
for nic in network_details: for nic in network_details:
if not isinstance(nic, service_base.NetworkDetails): if not isinstance(nic, service_base.NetworkDetails):
@ -53,7 +67,7 @@ def _preprocess_nics(network_details, network_adapters):
"invalid NetworkDetails object {!r}" "invalid NetworkDetails object {!r}"
.format(type(nic)) .format(type(nic))
) )
# check requirements # Check requirements.
final_status = True final_status = True
for fields, status in NET_REQUIRE.items(): for fields, status in NET_REQUIRE.items():
if not status: if not status:
@ -68,14 +82,14 @@ def _preprocess_nics(network_details, network_adapters):
# Complete hardware address if missing by selecting # Complete hardware address if missing by selecting
# the corresponding MAC in terms of naming, then ordering. # the corresponding MAC in terms of naming, then ordering.
if not nic.mac: if not nic.mac:
mac = None # By name...
# by name
macs = [adapter[1] for adapter in network_adapters macs = [adapter[1] for adapter in network_adapters
if adapter[0] == nic.name] if adapter[0] == nic.name]
mac = macs[0] if macs else None mac = macs[0] if macs else None
# or by order # ...or by order.
if not mac and ind < total: idx = _name2idx(nic.name)
mac = network_adapters[ind][1] if not mac and idx < total:
mac = network_adapters[idx][1]
nic = service_base.NetworkDetails( nic = service_base.NetworkDetails(
nic.name, nic.name,
mac, mac,
@ -86,7 +100,6 @@ def _preprocess_nics(network_details, network_adapters):
nic.dnsnameservers nic.dnsnameservers
) )
_network_details.append(nic) _network_details.append(nic)
ind += 1
return _network_details return _network_details

View File

@ -87,15 +87,13 @@ class TestNetworkConfigPlugin(unittest.TestCase):
reboot = len(missed_adapters) != self._count reboot = len(missed_adapters) != self._count
self.assertEqual((plugin_base.PLUGIN_EXECUTION_DONE, reboot), ret) 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 # Generate fake pairs of NetworkDetails objects and
# local ethernet network adapters. # local ethernet network adapters.
iface_name = "Ethernet" if wrong_names else "eth"
self._count = 3 self._count = 3
details_names = [ details_names = ["{}{}".format(iface_name, idx)
"eth0", for idx in range(self._count)]
"eth1",
"eth2"
]
if same_names: if same_names:
adapters_names = details_names[:] adapters_names = details_names[:]
else: else:
@ -120,7 +118,7 @@ class TestNetworkConfigPlugin(unittest.TestCase):
"192.168.255.255", "192.168.255.255",
"192.168.122.127", "192.168.122.127",
] ]
gateway = [ gateways = [
"192.168.122.1", "192.168.122.1",
"192.168.122.16", "192.168.122.16",
"192.168.122.32", "192.168.122.32",
@ -136,11 +134,11 @@ class TestNetworkConfigPlugin(unittest.TestCase):
adapter = (adapters_names[ind], macs[ind]) adapter = (adapters_names[ind], macs[ind])
nic = service_base.NetworkDetails( nic = service_base.NetworkDetails(
details_names[ind], details_names[ind],
macs[ind], None if no_macs else macs[ind],
addresses[ind], addresses[ind],
netmasks[ind], netmasks[ind],
broadcasts[ind], broadcasts[ind],
gateway[ind], gateways[ind],
dnsnses[ind].split() dnsnses[ind].split()
) )
self._network_adapters.append(adapter) self._network_adapters.append(adapter)
@ -166,6 +164,10 @@ class TestNetworkConfigPlugin(unittest.TestCase):
self._network_details.append([None] * 6) self._network_details.append([None] * 6)
self._partial_test_execute(invalid_details=True) 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): def test_execute_single(self):
for _ in range(self._count - 1): for _ in range(self._count - 1):
self._network_adapters.pop() self._network_adapters.pop()