Skip to content

Commit

Permalink
feat: Determining route metric based on NIC name (#5070)
Browse files Browse the repository at this point in the history
In the Alibaba Cloud scenario, we do not wish to define routing priority based on MAC addresses.
In a cloud environment where the kernel parameter net.ifnames=0 has been configured,
network interface card (NIC) names are determined by default according to their underlying Bus, Device, and Function (BDF) numbers,
incrementing from eth0 to ethN, with eth0 acting as the default primary NIC name.

In the previous logic, network-card has the highest priority, followed by device-number as the second priority.
When _fallback_nic_order is set to NicOrder.MAC, the mac address takes the third priority.
On the other hand, when _fallback_nic_order is set to NicOrder.NIC_NAME, the NIC name becomes the third priority.

In AWS environments, the default setting remains as _fallback_nic_order = NicOrder.MAC, maintaining the original behavior.
However, in Alibaba Cloud scenarios, we set _fallback_nic_order = NicOrder.NIC_NAME.
  • Loading branch information
ld9379435 authored Apr 8, 2024
1 parent ec384da commit 0a3a5e2
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 30 deletions.
7 changes: 6 additions & 1 deletion cloudinit/sources/DataSourceAliYun.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from cloudinit import dmi, sources
from cloudinit.event import EventScope, EventType
from cloudinit.sources import DataSourceEc2 as EC2
from cloudinit.sources import DataSourceHostname
from cloudinit.sources import DataSourceHostname, NicOrder

LOG = logging.getLogger(__name__)

Expand All @@ -32,6 +32,11 @@ def __init__(self, sys_cfg, distro, paths):
super(DataSourceAliYun, self).__init__(sys_cfg, distro, paths)
self.default_update_events = copy.deepcopy(self.default_update_events)
self.default_update_events[EventScope.NETWORK].add(EventType.BOOT)
self._fallback_nic_order = NicOrder.NIC_NAME

def _unpickle(self, ci_pkl_version: int) -> None:
super()._unpickle(ci_pkl_version)
self._fallback_nic_order = NicOrder.NIC_NAME

def get_hostname(self, fqdn=False, resolve_ip=False, metadata_only=False):
hostname = self.metadata.get("hostname")
Expand Down
29 changes: 22 additions & 7 deletions cloudinit/sources/DataSourceEc2.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from cloudinit.net import activators
from cloudinit.net.dhcp import NoDHCPLeaseError
from cloudinit.net.ephemeral import EphemeralIPNetwork
from cloudinit.sources import NicOrder
from cloudinit.sources.helpers import ec2

LOG = logging.getLogger(__name__)
Expand Down Expand Up @@ -118,10 +119,12 @@ def __init__(self, sys_cfg, distro, paths):
super(DataSourceEc2, self).__init__(sys_cfg, distro, paths)
self.metadata_address = None
self.identity = None
self._fallback_nic_order = NicOrder.MAC

def _unpickle(self, ci_pkl_version: int) -> None:
super()._unpickle(ci_pkl_version)
self.extra_hotplug_udev_rules = _EXTRA_HOTPLUG_UDEV_RULES
self._fallback_nic_order = NicOrder.MAC

def _get_cloud_name(self):
"""Return the cloud name as identified during _get_data."""
Expand Down Expand Up @@ -532,6 +535,7 @@ def network_config(self):
full_network_config=util.get_cfg_option_bool(
self.ds_cfg, "apply_full_imds_network_config", True
),
fallback_nic_order=self._fallback_nic_order,
)

# Non-VPC (aka Classic) Ec2 instances need to rewrite the
Expand Down Expand Up @@ -892,30 +896,35 @@ def _collect_platform_data():


def _build_nic_order(
macs_metadata: Dict[str, Dict], macs: List[str]
macs_metadata: Dict[str, Dict],
macs_to_nics: Dict[str, str],
fallback_nic_order: NicOrder = NicOrder.MAC,
) -> Dict[str, int]:
"""
Builds a dictionary containing macs as keys nad nic orders as values,
Builds a dictionary containing macs as keys and nic orders as values,
taking into account `network-card` and `device-number` if present.
Note that the first NIC will be the primary NIC as it will be the one with
[network-card] == 0 and device-number == 0 if present.
@param macs_metadata: dictionary with mac address as key and contents like:
{"device-number": "0", "interface-id": "...", "local-ipv4s": ...}
@macs: list of macs to consider
@macs_to_nics: dictionary with mac address as key and nic name as value
@return: Dictionary with macs as keys and nic orders as values.
"""
nic_order: Dict[str, int] = {}
if len(macs) == 0 or len(macs_metadata) == 0:
if len(macs_to_nics) == 0 or len(macs_metadata) == 0:
return nic_order

valid_macs_metadata = filter(
# filter out nics without metadata (not a physical nic)
lambda mmd: mmd[1] is not None,
# filter by macs
map(lambda mac: (mac, macs_metadata.get(mac)), macs),
map(
lambda mac: (mac, macs_metadata.get(mac), macs_to_nics[mac]),
macs_to_nics.keys(),
),
)

def _get_key_as_int_or(dikt, key, alt_value):
Expand All @@ -932,7 +941,7 @@ def _get_key_as_int_or(dikt, key, alt_value):
# function.
return {
mac: i
for i, (mac, _mac_metadata) in enumerate(
for i, (mac, _mac_metadata, _nic_name) in enumerate(
sorted(
valid_macs_metadata,
key=lambda mmd: (
Expand All @@ -942,6 +951,9 @@ def _get_key_as_int_or(dikt, key, alt_value):
_get_key_as_int_or(
mmd[1], "device-number", float("infinity")
),
mmd[2]
if fallback_nic_order == NicOrder.NIC_NAME
else mmd[0],
),
)
)
Expand Down Expand Up @@ -1030,6 +1042,7 @@ def convert_ec2_metadata_network_config(
macs_to_nics=None,
fallback_nic=None,
full_network_config=True,
fallback_nic_order=NicOrder.MAC,
):
"""Convert ec2 metadata to network config version 2 data dict.
Expand Down Expand Up @@ -1072,8 +1085,10 @@ def convert_ec2_metadata_network_config(
return netcfg
# Apply network config for all nics and any secondary IPv4/v6 addresses
is_netplan = distro.network_activator == activators.NetplanActivator
nic_order = _build_nic_order(
macs_metadata, macs_to_nics, fallback_nic_order
)
macs = sorted(macs_to_nics.keys())
nic_order = _build_nic_order(macs_metadata, macs)
for mac in macs:
nic_name = macs_to_nics[mac]
nic_metadata = macs_metadata.get(mac)
Expand Down
10 changes: 10 additions & 0 deletions cloudinit/sources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ def __str__(self) -> str:
return self.value


class NicOrder(Enum):
"""Represents ways to sort NICs"""

MAC = "mac"
NIC_NAME = "nic_name"

def __str__(self) -> str:
return self.value


class DatasourceUnpickleUserDataError(Exception):
"""Raised when userdata is unable to be unpickled due to python upgrades"""

Expand Down
Loading

0 comments on commit 0a3a5e2

Please sign in to comment.