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:
parent
0ccea78de6
commit
9319d74115
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user