Skip to content

Commit 4696502

Browse files
committed
net: NAD live-update connectivity test for Linux bridge
Implement test_connectivity (CNV-15972): after the NAD reference change moves iface-1 from VLAN-A to VLAN-B, verifies that the under-test VM gains TCP connectivity to the reference VM on VLAN-B and loses it on VLAN-A. Uses client_bind_dev and server_bind_dev to pin traffic to the correct interface, eliminating ECMP routing ambiguity. Implement test_two_networks (CNV-15946): applies a single atomic patch swapping both secondary networks simultaneously, then verifies each interface reaches its new VLAN. Adds the ref_vm fixture (module-scoped): a reference VM with iface-1 on NAD-VLAN-A and iface-2 on NAD-VLAN-B. Baseline connectivity is verified in baseline_connectivity before the first test runs. Signed-off-by: Asia Khromov <azhivovk@redhat.com> Assisted-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent 9c9aaed commit 4696502

3 files changed

Lines changed: 193 additions & 10 deletions

File tree

tests/network/l2_bridge/nad_ref_change/conftest.py

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@
44
from kubernetes.dynamic import DynamicClient
55
from ocp_resources.namespace import Namespace
66

7-
from libs.net.ip import random_cidr_addresses_by_family
7+
from libs.net.ip import filter_link_local_addresses, random_cidr_addresses_by_family
88
from libs.net.netattachdef import CNIPluginBridgeConfig, NetConfig, NetworkAttachmentDefinition
9-
from libs.net.vmspec import wait_for_ifaces_status
9+
from libs.net.vmspec import lookup_iface_status, wait_for_ifaces_status
1010
from libs.vm.vm import BaseVirtualMachine
1111
from tests.network.l2_bridge.libl2bridge import LINUX_BRIDGE_IFACE_NAME_1, LINUX_BRIDGE_IFACE_NAME_2
1212
from tests.network.l2_bridge.nad_ref_change.lib_helpers import (
13+
GUEST_IFACE_1,
14+
GUEST_IFACE_2,
1315
NET_SEED,
1416
two_secondary_bridge_vm,
1517
)
1618
from tests.network.libs import nodenetworkconfigurationpolicy as libnncp
17-
from tests.network.libs.connectivity import ARP_ISOLATION_SYSCTL_CMD
19+
from tests.network.libs.connectivity import ARP_ISOLATION_SYSCTL_CMD, poll_tcp_connectivity
1820

1921

2022
@pytest.fixture(scope="module")
@@ -55,6 +57,36 @@ def bridge_nad_b(
5557
yield nad
5658

5759

60+
@pytest.fixture(scope="module")
61+
def ref_vm(
62+
namespace: Namespace,
63+
unprivileged_client: DynamicClient,
64+
bridge_nad_a: NetworkAttachmentDefinition,
65+
bridge_nad_b: NetworkAttachmentDefinition,
66+
) -> Generator[BaseVirtualMachine]:
67+
iface_a_ips = random_cidr_addresses_by_family(net_seed=NET_SEED, host_address=1)
68+
iface_b_ips = random_cidr_addresses_by_family(net_seed=NET_SEED, host_address=2)
69+
with two_secondary_bridge_vm(
70+
namespace=namespace.name,
71+
name="ref-vm",
72+
client=unprivileged_client,
73+
nad_names=[bridge_nad_a.name, bridge_nad_b.name],
74+
ip_addresses=[iface_a_ips, iface_b_ips],
75+
iface_names=[LINUX_BRIDGE_IFACE_NAME_1, LINUX_BRIDGE_IFACE_NAME_2],
76+
runcmd=ARP_ISOLATION_SYSCTL_CMD,
77+
) as vm:
78+
vm.start(wait=True)
79+
vm.wait_for_agent_connected()
80+
wait_for_ifaces_status(
81+
vm=vm,
82+
ip_addresses_by_spec_net_name={
83+
LINUX_BRIDGE_IFACE_NAME_1: [addr.split("/")[0] for addr in iface_a_ips],
84+
LINUX_BRIDGE_IFACE_NAME_2: [addr.split("/")[0] for addr in iface_b_ips],
85+
},
86+
)
87+
yield vm
88+
89+
5890
@pytest.fixture(scope="class")
5991
def under_test_vm_two_ifaces(
6092
namespace: Namespace,
@@ -82,3 +114,34 @@ def under_test_vm_two_ifaces(
82114
},
83115
)
84116
yield vm
117+
118+
119+
@pytest.fixture(scope="class")
120+
def baseline_connectivity(
121+
under_test_vm_two_ifaces: BaseVirtualMachine,
122+
ref_vm: BaseVirtualMachine,
123+
) -> None:
124+
"""Verify baseline connectivity before the NAD reference change.
125+
126+
Asserts that the under-test VM can reach the reference VM on VLAN-A (iface-1)
127+
and cannot reach it on VLAN-B (iface-2) before any NAD update is applied.
128+
"""
129+
for server_ip in filter_link_local_addresses(
130+
ip_addresses=lookup_iface_status(vm=ref_vm, iface_name=LINUX_BRIDGE_IFACE_NAME_1).ipAddresses
131+
):
132+
poll_tcp_connectivity(
133+
client_vm=under_test_vm_two_ifaces,
134+
server_vm=ref_vm,
135+
server_ip=str(server_ip),
136+
server_bind_dev=GUEST_IFACE_1,
137+
)
138+
for server_ip in filter_link_local_addresses(
139+
ip_addresses=lookup_iface_status(vm=ref_vm, iface_name=LINUX_BRIDGE_IFACE_NAME_2).ipAddresses
140+
):
141+
poll_tcp_connectivity(
142+
client_vm=under_test_vm_two_ifaces,
143+
server_vm=ref_vm,
144+
server_ip=str(server_ip),
145+
server_bind_dev=GUEST_IFACE_2,
146+
expect_connectivity=False,
147+
)

tests/network/l2_bridge/nad_ref_change/lib_helpers.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from libs.vm.vm import BaseVirtualMachine, add_volume_disk, cloudinitdisk_storage
1616
from tests.network.libs import cloudinit
1717
from tests.network.libs.cloudinit import primary_iface_cloud_init
18+
from tests.network.libs.connectivity import poll_tcp_connectivity
1819

1920
NET_SEED: Final[int] = 0
2021

@@ -23,6 +24,57 @@
2324
GUEST_IFACE_2: Final[str] = "eth2"
2425

2526

27+
def assert_connectivity(
28+
client_vm: BaseVirtualMachine,
29+
server_vm: BaseVirtualMachine,
30+
server_ip: str,
31+
server_bind_dev: str,
32+
client_bind_dev: str,
33+
) -> None:
34+
"""Assert TCP connectivity from client to server for a single IP address.
35+
36+
Args:
37+
client_vm: VM initiating the connection.
38+
server_vm: VM accepting the connection.
39+
server_ip: IP address to connect to.
40+
server_bind_dev: Guest device to bind the iperf3 server to (bypasses ECMP).
41+
client_bind_dev: Guest device to bind the iperf3 client to (bypasses ECMP).
42+
"""
43+
poll_tcp_connectivity(
44+
client_vm=client_vm,
45+
server_vm=server_vm,
46+
server_ip=server_ip,
47+
client_bind_dev=client_bind_dev,
48+
server_bind_dev=server_bind_dev,
49+
)
50+
51+
52+
def assert_no_connectivity(
53+
client_vm: BaseVirtualMachine,
54+
server_vm: BaseVirtualMachine,
55+
server_ip: str,
56+
server_bind_dev: str,
57+
client_bind_dev: str,
58+
) -> None:
59+
"""Assert no TCP connectivity from client to server for a single IP address.
60+
61+
Args:
62+
client_vm: VM initiating the connection.
63+
server_vm: VM accepting the connection.
64+
server_ip: IP address to connect to.
65+
server_bind_dev: Guest device to bind the iperf3 server to (bypasses ECMP).
66+
client_bind_dev: Guest device to bind the iperf3 client to (bypasses ECMP).
67+
"""
68+
poll_tcp_connectivity(
69+
client_vm=client_vm,
70+
server_vm=server_vm,
71+
server_ip=server_ip,
72+
client_bind_dev=client_bind_dev,
73+
server_bind_dev=server_bind_dev,
74+
expect_connectivity=False,
75+
)
76+
77+
2678
def update_nad_references(vm: BaseVirtualMachine, nad_name_by_net: dict[str, str]) -> None:
2779
"""Update secondary network NAD references and wait for the change to be fully applied.
2880

tests/network/l2_bridge/nad_ref_change/test_nad_ref_change.py

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,19 @@
1313

1414
import pytest
1515

16+
from libs.net.ip import filter_link_local_addresses
1617
from libs.net.vmspec import lookup_iface_status
17-
from tests.network.l2_bridge.libl2bridge import LINUX_BRIDGE_IFACE_NAME_1
18+
from tests.network.l2_bridge.libl2bridge import LINUX_BRIDGE_IFACE_NAME_1, LINUX_BRIDGE_IFACE_NAME_2
1819
from tests.network.l2_bridge.nad_ref_change.lib_helpers import (
20+
GUEST_IFACE_1,
21+
GUEST_IFACE_2,
22+
assert_connectivity,
23+
assert_no_connectivity,
1924
update_nad_references,
2025
)
2126

2227

28+
@pytest.mark.usefixtures("baseline_connectivity")
2329
@pytest.mark.incremental
2430
class TestRunningVMLinuxBridgeVlanChange:
2531
"""
@@ -80,7 +86,7 @@ def test_vm_state_iface_info_preserved(
8086
8187
Expected:
8288
- Under-test VM remains running after the NAD reference change
83-
- Guest first secondary interface MAC address, name, and IP addresses are the same before and after the
89+
- All guest first secondary interface status fields are identical before and after the
8490
NAD reference change
8591
"""
8692
iface_before = lookup_iface_status(vm=under_test_vm_two_ifaces, iface_name=LINUX_BRIDGE_IFACE_NAME_1)
@@ -95,7 +101,14 @@ def test_vm_state_iface_info_preserved(
95101
)
96102

97103
@pytest.mark.polarion("CNV-15972")
98-
def test_connectivity(self):
104+
def test_connectivity(
105+
self,
106+
subtests,
107+
under_test_vm_two_ifaces,
108+
ref_vm,
109+
bridge_nad_a,
110+
bridge_nad_b,
111+
):
99112
"""
100113
Test that the under-test VM has TCP connectivity to the reference VM on the new NAD-VLAN-B and no TCP
101114
connectivity on the old NAD-VLAN-A after the NAD reference change.
@@ -112,11 +125,38 @@ def test_connectivity(self):
112125
- Under-test VM eventually has TCP connectivity to the reference VM on NAD-VLAN-B
113126
- Under-test VM has no TCP connectivity to the reference VM on NAD-VLAN-A
114127
"""
115-
116-
test_connectivity.__test__ = False
128+
for server_ip in filter_link_local_addresses(
129+
ip_addresses=lookup_iface_status(vm=ref_vm, iface_name=LINUX_BRIDGE_IFACE_NAME_2).ipAddresses
130+
):
131+
with subtests.test(msg=f"IPv{server_ip.version} on {bridge_nad_b.name}"):
132+
assert_connectivity(
133+
client_vm=under_test_vm_two_ifaces,
134+
server_vm=ref_vm,
135+
server_ip=str(server_ip),
136+
server_bind_dev=GUEST_IFACE_2,
137+
client_bind_dev=GUEST_IFACE_1,
138+
)
139+
for server_ip in filter_link_local_addresses(
140+
ip_addresses=lookup_iface_status(vm=ref_vm, iface_name=LINUX_BRIDGE_IFACE_NAME_1).ipAddresses
141+
):
142+
with subtests.test(msg=f"IPv{server_ip.version} on {bridge_nad_a.name}"):
143+
assert_no_connectivity(
144+
client_vm=under_test_vm_two_ifaces,
145+
server_vm=ref_vm,
146+
server_ip=str(server_ip),
147+
server_bind_dev=GUEST_IFACE_1,
148+
client_bind_dev=GUEST_IFACE_1,
149+
)
117150

118151
@pytest.mark.polarion("CNV-15946")
119-
def test_two_networks(self):
152+
def test_two_networks(
153+
self,
154+
subtests,
155+
under_test_vm_two_ifaces,
156+
ref_vm,
157+
bridge_nad_a,
158+
bridge_nad_b,
159+
):
120160
"""
121161
Test that both secondary Linux bridge networks on a running VM can have their VLANs
122162
changed in a single patch, with each network switching to a different VLAN.
@@ -140,8 +180,36 @@ def test_two_networks(self):
140180
- Under-test VM first secondary network eventually has TCP connectivity to the reference VM on NAD-VLAN-A
141181
- Under-test VM second secondary network eventually has TCP connectivity to the reference VM on NAD-VLAN-B
142182
"""
183+
update_nad_references(
184+
vm=under_test_vm_two_ifaces,
185+
nad_name_by_net={
186+
LINUX_BRIDGE_IFACE_NAME_1: bridge_nad_a.name,
187+
LINUX_BRIDGE_IFACE_NAME_2: bridge_nad_b.name,
188+
},
189+
)
143190

144-
test_two_networks.__test__ = False
191+
for server_ip in filter_link_local_addresses(
192+
ip_addresses=lookup_iface_status(vm=ref_vm, iface_name=LINUX_BRIDGE_IFACE_NAME_1).ipAddresses
193+
):
194+
with subtests.test(msg=f"IPv{server_ip.version} on {bridge_nad_a.name}"):
195+
assert_connectivity(
196+
client_vm=under_test_vm_two_ifaces,
197+
server_vm=ref_vm,
198+
server_ip=str(server_ip),
199+
server_bind_dev=GUEST_IFACE_1,
200+
client_bind_dev=GUEST_IFACE_1,
201+
)
202+
for server_ip in filter_link_local_addresses(
203+
ip_addresses=lookup_iface_status(vm=ref_vm, iface_name=LINUX_BRIDGE_IFACE_NAME_2).ipAddresses
204+
):
205+
with subtests.test(msg=f"IPv{server_ip.version} on {bridge_nad_b.name}"):
206+
assert_connectivity(
207+
client_vm=under_test_vm_two_ifaces,
208+
server_vm=ref_vm,
209+
server_ip=str(server_ip),
210+
server_bind_dev=GUEST_IFACE_2,
211+
client_bind_dev=GUEST_IFACE_2,
212+
)
145213

146214

147215
@pytest.mark.polarion("CNV-15947")

0 commit comments

Comments
 (0)