Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 64 additions & 7 deletions cloudbaseinit/plugins/common/networkconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# under the License.

import re
import time

import netaddr
from oslo_log import log as oslo_logging
Expand Down Expand Up @@ -198,11 +199,27 @@ def _process_physical_links(osutils, network_details):
link.mac_address)

if adapter_name != link.name:
LOG.info(
"Renaming network adapter \"%(old_name)s\" to "
"\"%(new_name)s\"",
{"old_name": adapter_name, "new_name": link.name})
osutils.rename_network_adapter(adapter_name, link.name)
if link.id != link.name:
# set-name was explicitly provided, honor the rename
LOG.info(
"Renaming network adapter \"%(old_name)s\" to "
"\"%(new_name)s\"",
{"old_name": adapter_name, "new_name": link.name})
osutils.rename_network_adapter(adapter_name, link.name)
else:
# No set-name, use the actual adapter name to avoid
# unreliable WMI rename (see issues #82, #101, #151)
LOG.info(
"Network adapter \"%(adapter_name)s\" found by "
"MAC for config name \"%(config_name)s\", "
"skipping rename",
{"adapter_name": adapter_name,
"config_name": link.name})
for idx, net in enumerate(network_details.networks):
if net.link == link.name:
network_details.networks[idx] = net._replace(
link=adapter_name)
link = link._replace(name=adapter_name)

NetworkConfigPlugin._process_link_common(osutils, link)

Expand Down Expand Up @@ -259,7 +276,16 @@ def _process_networks(osutils, network_details):
ipv4_ns, ipv6_ns = NetworkConfigPlugin._get_default_dns_nameservers(
network_details)

# Build name -> MAC map for re-lookup on adapter disappearance
# (handles vDPA/SR-IOV reactivation during boot)
mac_by_name = {}
for link in network_details.links:
if link.mac_address and \
link.type == network_model.LINK_TYPE_PHYSICAL:
mac_by_name[link.name] = link.mac_address

for net in network_details.networks:
adapter_name = net.link
ip_address, prefix_len = net.address_cidr.split("/")

gateway = None
Expand All @@ -276,14 +302,45 @@ def _process_networks(osutils, network_details):
else:
nameservers = ipv4_ns

# Re-lookup adapter by MAC if available, with retry for
# transient disappearance (e.g. vDPA/SR-IOV reactivation)
if adapter_name in mac_by_name:
mac = mac_by_name[adapter_name]
for attempt in range(10):
try:
current_name = \
osutils.get_network_adapter_name_by_mac_address(
mac)
if current_name != adapter_name:
LOG.info(
"Adapter name changed from "
"\"%(old)s\" to \"%(new)s\"",
{"old": adapter_name, "new": current_name})
adapter_name = current_name
break
except exception.ItemNotFoundException:
if attempt < 9:
LOG.warning(
"Adapter with MAC %(mac)s not found "
"(attempt %(n)d/10), retrying in 3s",
{"mac": mac, "n": attempt + 1})
time.sleep(3)
else:
LOG.error(
"Adapter with MAC %(mac)s not found "
"after 10 attempts",
{"mac": mac})
raise

LOG.info(
"Setting static IP configuration on network adapter "
"\"%(name)s\". IP: %(ip)s, prefix length: %(prefix_len)s, "
"gateway: %(gateway)s, dns: %(dns)s",
{"name": net.link, "ip": ip_address, "prefix_len": prefix_len,
{"name": adapter_name, "ip": ip_address,
"prefix_len": prefix_len,
"gateway": gateway, "dns": nameservers})
reboot = osutils.set_static_network_config(
net.link, ip_address, prefix_len, gateway, nameservers)
adapter_name, ip_address, prefix_len, gateway, nameservers)
reboot_required = reboot or reboot_required

return reboot_required
Expand Down
93 changes: 93 additions & 0 deletions cloudbaseinit/tests/plugins/common/test_networkconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,3 +473,96 @@ def test_execute_network_details_v2_ipv4_dns_list(self):

def test_execute_network_details_v2_ipv6_dns_list(self):
self._test_execute_network_details_v2(both_ipv6_dns_list=True)

@mock.patch("cloudbaseinit.osutils.factory.get_os_utils")
def test_execute_network_details_v2_no_rename_without_set_name(
self, mock_get_os_utils):
"""When link.id == link.name (no set-name), skip adapter rename."""
link1 = network_model.Link(
id="eth0",
name="eth0",
type=network_model.LINK_TYPE_PHYSICAL,
enabled=True,
mac_address=u"00:00:00:00:00:01",
mtu=1500,
bond=None,
vlan_link=None,
vlan_id=None)

route1 = network_model.Route(
network_cidr=u"0.0.0.0/0",
gateway=u"10.0.0.254")

network1 = network_model.Network(
link="eth0",
address_cidr=u"10.0.0.1/24",
dns_nameservers=["10.0.0.1"],
routes=[route1])

network_details = network_model.NetworkDetailsV2(
links=[link1],
networks=[network1],
services=[])

service = mock.Mock()
service.get_network_details_v2.return_value = network_details

mock_os_utils = mock.Mock()
mock_get_os_utils.return_value = mock_os_utils
mock_os_utils.get_network_adapter_name_by_mac_address.return_value = \
"Ethernet 3"

plugin = networkconfig.NetworkConfigPlugin()
plugin.execute(service, {})

# Should NOT rename when link.id == link.name (no set-name)
mock_os_utils.rename_network_adapter.assert_not_called()

# Should use actual adapter name for all operations
mock_os_utils.enable_network_adapter.assert_called_once_with(
"Ethernet 3", True)
mock_os_utils.set_network_adapter_mtu.assert_called_once_with(
"Ethernet 3", 1500)
mock_os_utils.set_static_network_config.assert_called_once_with(
"Ethernet 3", "10.0.0.1", "24", "10.0.0.254", ["10.0.0.1"])

@mock.patch("cloudbaseinit.osutils.factory.get_os_utils")
def test_execute_network_details_v2_rename_with_set_name(
self, mock_get_os_utils):
"""When link.id != link.name (set-name used), rename adapter."""
link1 = network_model.Link(
id="eth0",
name="my-nic",
type=network_model.LINK_TYPE_PHYSICAL,
enabled=True,
mac_address=u"00:00:00:00:00:01",
mtu=None,
bond=None,
vlan_link=None,
vlan_id=None)

network1 = network_model.Network(
link="my-nic",
address_cidr=u"10.0.0.1/24",
dns_nameservers=["10.0.0.1"],
routes=[])

network_details = network_model.NetworkDetailsV2(
links=[link1],
networks=[network1],
services=[])

service = mock.Mock()
service.get_network_details_v2.return_value = network_details

mock_os_utils = mock.Mock()
mock_get_os_utils.return_value = mock_os_utils
mock_os_utils.get_network_adapter_name_by_mac_address.return_value = \
"Ethernet"

plugin = networkconfig.NetworkConfigPlugin()
plugin.execute(service, {})

# Should rename when link.id != link.name (set-name was used)
mock_os_utils.rename_network_adapter.assert_called_once_with(
"Ethernet", "my-nic")