Skip to content

Commit b983b1d

Browse files
committed
Skip adapter rename when set-name is not explicitly provided
When using network-config v2 with match.macaddress, cloudbase-init always renames the adapter to the ethernets dict key name. This causes failures on Windows because: 1. The WMI rename call (MSFT_NetAdapter.rename) is unreliable during early boot — the new name is not immediately queryable, causing "Network interface not found" errors (#101, #151) 2. The rename can hang for minutes on some Windows versions (#82) 3. Multiple adapters can cause name collisions This aligns with cloud-init (Linux) behavior where the ethernets dict key is just an opaque identifier when match is present, and only set-name triggers an actual rename. The fix checks link.id vs link.name: when they match (no set-name was provided), the adapter is used with its current Windows name instead of being renamed. When set-name is explicitly provided (link.id != link.name), rename proceeds as before. Signed-off-by: Max Makarov <maxpain@linux.com>
1 parent d63509f commit b983b1d

File tree

2 files changed

+157
-7
lines changed

2 files changed

+157
-7
lines changed

cloudbaseinit/plugins/common/networkconfig.py

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# under the License.
1414

1515
import re
16+
import time
1617

1718
import netaddr
1819
from oslo_log import log as oslo_logging
@@ -198,11 +199,27 @@ def _process_physical_links(osutils, network_details):
198199
link.mac_address)
199200

200201
if adapter_name != link.name:
201-
LOG.info(
202-
"Renaming network adapter \"%(old_name)s\" to "
203-
"\"%(new_name)s\"",
204-
{"old_name": adapter_name, "new_name": link.name})
205-
osutils.rename_network_adapter(adapter_name, link.name)
202+
if link.id != link.name:
203+
# set-name was explicitly provided, honor the rename
204+
LOG.info(
205+
"Renaming network adapter \"%(old_name)s\" to "
206+
"\"%(new_name)s\"",
207+
{"old_name": adapter_name, "new_name": link.name})
208+
osutils.rename_network_adapter(adapter_name, link.name)
209+
else:
210+
# No set-name, use the actual adapter name to avoid
211+
# unreliable WMI rename (see issues #82, #101, #151)
212+
LOG.info(
213+
"Network adapter \"%(adapter_name)s\" found by "
214+
"MAC for config name \"%(config_name)s\", "
215+
"skipping rename",
216+
{"adapter_name": adapter_name,
217+
"config_name": link.name})
218+
for idx, net in enumerate(network_details.networks):
219+
if net.link == link.name:
220+
network_details.networks[idx] = net._replace(
221+
link=adapter_name)
222+
link = link._replace(name=adapter_name)
206223

207224
NetworkConfigPlugin._process_link_common(osutils, link)
208225

@@ -259,7 +276,16 @@ def _process_networks(osutils, network_details):
259276
ipv4_ns, ipv6_ns = NetworkConfigPlugin._get_default_dns_nameservers(
260277
network_details)
261278

279+
# Build name -> MAC map for re-lookup on adapter disappearance
280+
# (handles vDPA/SR-IOV reactivation during boot)
281+
mac_by_name = {}
282+
for link in network_details.links:
283+
if link.mac_address and \
284+
link.type == network_model.LINK_TYPE_PHYSICAL:
285+
mac_by_name[link.name] = link.mac_address
286+
262287
for net in network_details.networks:
288+
adapter_name = net.link
263289
ip_address, prefix_len = net.address_cidr.split("/")
264290

265291
gateway = None
@@ -276,14 +302,45 @@ def _process_networks(osutils, network_details):
276302
else:
277303
nameservers = ipv4_ns
278304

305+
# Re-lookup adapter by MAC if available, with retry for
306+
# transient disappearance (e.g. vDPA/SR-IOV reactivation)
307+
if adapter_name in mac_by_name:
308+
mac = mac_by_name[adapter_name]
309+
for attempt in range(10):
310+
try:
311+
current_name = \
312+
osutils.get_network_adapter_name_by_mac_address(
313+
mac)
314+
if current_name != adapter_name:
315+
LOG.info(
316+
"Adapter name changed from "
317+
"\"%(old)s\" to \"%(new)s\"",
318+
{"old": adapter_name, "new": current_name})
319+
adapter_name = current_name
320+
break
321+
except exception.ItemNotFoundException:
322+
if attempt < 9:
323+
LOG.warning(
324+
"Adapter with MAC %(mac)s not found "
325+
"(attempt %(n)d/10), retrying in 3s",
326+
{"mac": mac, "n": attempt + 1})
327+
time.sleep(3)
328+
else:
329+
LOG.error(
330+
"Adapter with MAC %(mac)s not found "
331+
"after 10 attempts",
332+
{"mac": mac})
333+
raise
334+
279335
LOG.info(
280336
"Setting static IP configuration on network adapter "
281337
"\"%(name)s\". IP: %(ip)s, prefix length: %(prefix_len)s, "
282338
"gateway: %(gateway)s, dns: %(dns)s",
283-
{"name": net.link, "ip": ip_address, "prefix_len": prefix_len,
339+
{"name": adapter_name, "ip": ip_address,
340+
"prefix_len": prefix_len,
284341
"gateway": gateway, "dns": nameservers})
285342
reboot = osutils.set_static_network_config(
286-
net.link, ip_address, prefix_len, gateway, nameservers)
343+
adapter_name, ip_address, prefix_len, gateway, nameservers)
287344
reboot_required = reboot or reboot_required
288345

289346
return reboot_required

cloudbaseinit/tests/plugins/common/test_networkconfig.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,3 +473,96 @@ def test_execute_network_details_v2_ipv4_dns_list(self):
473473

474474
def test_execute_network_details_v2_ipv6_dns_list(self):
475475
self._test_execute_network_details_v2(both_ipv6_dns_list=True)
476+
477+
@mock.patch("cloudbaseinit.osutils.factory.get_os_utils")
478+
def test_execute_network_details_v2_no_rename_without_set_name(
479+
self, mock_get_os_utils):
480+
"""When link.id == link.name (no set-name), skip adapter rename."""
481+
link1 = network_model.Link(
482+
id="eth0",
483+
name="eth0",
484+
type=network_model.LINK_TYPE_PHYSICAL,
485+
enabled=True,
486+
mac_address=u"00:00:00:00:00:01",
487+
mtu=1500,
488+
bond=None,
489+
vlan_link=None,
490+
vlan_id=None)
491+
492+
route1 = network_model.Route(
493+
network_cidr=u"0.0.0.0/0",
494+
gateway=u"10.0.0.254")
495+
496+
network1 = network_model.Network(
497+
link="eth0",
498+
address_cidr=u"10.0.0.1/24",
499+
dns_nameservers=["10.0.0.1"],
500+
routes=[route1])
501+
502+
network_details = network_model.NetworkDetailsV2(
503+
links=[link1],
504+
networks=[network1],
505+
services=[])
506+
507+
service = mock.Mock()
508+
service.get_network_details_v2.return_value = network_details
509+
510+
mock_os_utils = mock.Mock()
511+
mock_get_os_utils.return_value = mock_os_utils
512+
mock_os_utils.get_network_adapter_name_by_mac_address.return_value = \
513+
"Ethernet 3"
514+
515+
plugin = networkconfig.NetworkConfigPlugin()
516+
plugin.execute(service, {})
517+
518+
# Should NOT rename when link.id == link.name (no set-name)
519+
mock_os_utils.rename_network_adapter.assert_not_called()
520+
521+
# Should use actual adapter name for all operations
522+
mock_os_utils.enable_network_adapter.assert_called_once_with(
523+
"Ethernet 3", True)
524+
mock_os_utils.set_network_adapter_mtu.assert_called_once_with(
525+
"Ethernet 3", 1500)
526+
mock_os_utils.set_static_network_config.assert_called_once_with(
527+
"Ethernet 3", "10.0.0.1", "24", "10.0.0.254", ["10.0.0.1"])
528+
529+
@mock.patch("cloudbaseinit.osutils.factory.get_os_utils")
530+
def test_execute_network_details_v2_rename_with_set_name(
531+
self, mock_get_os_utils):
532+
"""When link.id != link.name (set-name used), rename adapter."""
533+
link1 = network_model.Link(
534+
id="eth0",
535+
name="my-nic",
536+
type=network_model.LINK_TYPE_PHYSICAL,
537+
enabled=True,
538+
mac_address=u"00:00:00:00:00:01",
539+
mtu=None,
540+
bond=None,
541+
vlan_link=None,
542+
vlan_id=None)
543+
544+
network1 = network_model.Network(
545+
link="my-nic",
546+
address_cidr=u"10.0.0.1/24",
547+
dns_nameservers=["10.0.0.1"],
548+
routes=[])
549+
550+
network_details = network_model.NetworkDetailsV2(
551+
links=[link1],
552+
networks=[network1],
553+
services=[])
554+
555+
service = mock.Mock()
556+
service.get_network_details_v2.return_value = network_details
557+
558+
mock_os_utils = mock.Mock()
559+
mock_get_os_utils.return_value = mock_os_utils
560+
mock_os_utils.get_network_adapter_name_by_mac_address.return_value = \
561+
"Ethernet"
562+
563+
plugin = networkconfig.NetworkConfigPlugin()
564+
plugin.execute(service, {})
565+
566+
# Should rename when link.id != link.name (set-name was used)
567+
mock_os_utils.rename_network_adapter.assert_called_once_with(
568+
"Ethernet", "my-nic")

0 commit comments

Comments
 (0)