Skip to content

Commit

Permalink
tests: Prefer permanent MAC for finding link info, fallback to curren…
Browse files Browse the repository at this point in the history
…t MAC

When a network connection specifies both an interface name and MAC
address, and the physical interface has identical permanent and current
MACs, applying the configuration multiple times can cause an error:
"no such interface exists."

The issue occurs because `SysUtil.link_info_find()` may return a link
info entry where the user-specified MAC matches only the current
("address") but not the permanent MAC ("perm-address"). In such cases,
the returned link info may have a different interface name than the one
specified in the network connection, leading to the error.

We already implemented a fix (c341683) that ensures link lookup
prioritizes the permanent MAC, only falling back to the current MAC when
necessary. The integration test `tests_mac_address_match.yml` verifies
this fix by requiring an Ethernet interface where the permanent and
current MACs match.

Resolves: https://issues.redhat.com/browse/RHEL-74211

Signed-off-by: Wen Liang <[email protected]>
  • Loading branch information
liangwen12year authored and Wen Liang committed Feb 12, 2025
1 parent 894645b commit c3df88f
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 0 deletions.
1 change: 1 addition & 0 deletions tests/ensure_provider_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"playbooks/tests_infiniband.yml": {},
"playbooks/tests_ipv6_disabled.yml": {},
"playbooks/tests_ipv6_dns_search.yml": {},
"playbooks/tests_mac_address_match.yml": {},
"playbooks/tests_provider.yml": {
MINIMUM_VERSION: "'1.20.0'",
"comment": "# NetworKmanager 1.20.0 added support for forgetting profiles",
Expand Down
75 changes: 75 additions & 0 deletions tests/playbooks/tests_mac_address_match.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- name: Play for testing MAC address match on device
hosts: all
vars:
# This test requires an Ethernet interface whose permanent
# MAC address matches its current MAC address. Ensure that the
# specified interface meets this condition.
#
# Two VLAN profiles are defined to test deterministic behavior when fetching
# link information from sysfs. The issue being tested arises when `os.listdir(path)`
# returns interfaces in an arbitrary order, potentially listing a VLAN device
# before its parent interface. This can cause intermittent "no such interface
# exists" errors when applying configurations repeatedly.
#
# - `vlan_profile1` (e.g., `eth1.3732`) is named with the VLAN ID appended
# after the parent interface, following the standard `<parent>.<vlan_id>` format.
# - `vlan_profile2` (e.g., `120-vlan`) has a fixed name, designed to test a scenario
# where lexicographic sorting causes the VLAN to appear before its parent interface.
interface: "{{ lookup('env', 'MAC_ADDR_MATCH_INTERFACE') | default('eth1', true) }}"
profile: "{{ interface }}"
vlan_profile1: "{{ interface }}.3732"
vlan_profile2: "120-vlan"
lsr_fail_debug:
- __network_connections_result
tags:
- "tests::match"
tasks:
- name: Show playbook name
debug:
msg: "this is: playbooks/tests_mac_address_match.yml"
tags:
- always

- name: Install ethtool (test dependency)
package:
name: ethtool
state: present
use: "{{ (__network_is_ostree | d(false)) |
ternary('ansible.posix.rhel_rpm_ostree', omit) }}"

- name: Retrieve MAC address using ethtool
command: ethtool -P {{ interface }}
register: mac_address_result
changed_when: false
failed_when: mac_address_result.rc != 0

- name: Set the MAC address variable
set_fact:
mac: "{{ mac_address_result.stdout_lines[-1].split(' ')[-1] }}"

- name: Display the retrieved MAC address
debug:
msg: "Retrieved MAC address for {{ interface }}: {{ mac }}"

- name: Test the MAC address match
tags:
- tests::match:match
block:
- name: Include the task 'run_test.yml'
include_tasks: tasks/run_test.yml
vars:
lsr_description: Test MAC address match on device
lsr_setup:
- tasks/remove_profile.yml
- tasks/assert_profile_absent.yml
lsr_test:
- tasks/create_mac_address_match.yml
- tasks/create_mac_address_match.yml
lsr_assert:
- tasks/assert_profile_present.yml
- tasks/assert_network_connections_succeeded.yml
lsr_cleanup:
- tasks/cleanup_vlan_and_parent_profile+device.yml
- tasks/check_network_dns.yml
8 changes: 8 additions & 0 deletions tests/tasks/assert_network_connections_succeeded.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- name: Assert that configuring network connections is succeeded
assert:
that:
- __network_connections_result.failed == false
msg: Configuring network connections is failed with the error
"{{ __network_connections_result.stderr }}"
26 changes: 26 additions & 0 deletions tests/tasks/cleanup_vlan_and_parent_profile+device.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- name: Clean up the test devices and the connection profiles
tags:
- "tests::cleanup"
block:
- name: Import network role
import_role:
name: linux-system-roles.network
vars:
network_connections:
- name: "{{ profile }}"
persistent_state: absent
state: down
- name: "{{ vlan_profile1 }}"
persistent_state: absent
state: down
- name: "{{ vlan_profile2 }}"
persistent_state: absent
state: down
failed_when: false
- name: Delete the device '{{ interface }}'
command: ip link del {{ interface }}
failed_when: false
changed_when: false
...
53 changes: 53 additions & 0 deletions tests/tasks/create_mac_address_match.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# SPDX-License-Identifier: BSD-3-Clause
---
- name: Include network role
include_role:
name: linux-system-roles.network
vars:
network_connections:
- name: "{{ interface }}"
state: up
persistent_state: present
autoconnect: true
type: ethernet
interface_name: "{{ interface }}"
mac: "{{ mac }}"
ip:
dhcp4: false
auto6: false

- name: "{{ vlan_profile1 }}"
state: up
persistent_state: present
type: vlan
parent: "{{ interface }}"
vlan:
id: 3732
autoconnect: true
ip:
auto_gateway: false
ipv6_disabled: true
gateway4: 10.10.0.1
address: 10.10.0.6/24
dhcp4: false
auto6: false

- name: "{{ vlan_profile2 }}"
state: up
persistent_state: present
type: vlan
parent: "{{ interface }}"
vlan:
id: 120
autoconnect: true
ip:
auto_gateway: false
ipv6_disabled: true
gateway4: 10.10.120.1
address: 10.10.120.120/24
dhcp4: false
auto6: false
- name: Show result
debug:
var: __network_connections_result
...
23 changes: 23 additions & 0 deletions tests/tests_mac_address_match_nm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# SPDX-License-Identifier: BSD-3-Clause
# This file was generated by ensure_provider_tests.py
---
# set network provider and gather facts
# yamllint disable rule:line-length
- name: Run playbook 'playbooks/tests_mac_address_match.yml' with nm as provider
hosts: all
tasks:
- name: Include the task 'el_repo_setup.yml'
include_tasks: tasks/el_repo_setup.yml
- name: Set network provider to 'nm'
set_fact:
network_provider: nm
tags:
- always


# The test requires or should run with NetworkManager, therefore it cannot run
# on RHEL/CentOS 6
- name: Import the playbook 'playbooks/tests_mac_address_match.yml'
import_playbook: playbooks/tests_mac_address_match.yml
when:
- ansible_distribution_major_version != '6'

0 comments on commit c3df88f

Please sign in to comment.