Skip to content

Commit 9c9aaed

Browse files
committed
net, libs: add poll_tcp_connectivity helper
A NAD reference change triggers a live migration. During and immediately after the migration, ARP/NDP tables are rebuilding and the interface is reconfiguring on the new VLAN, so network reachability does not appear (or disappear) instantaneously. A one-shot TCP check after the patch would produce false negatives on the new VLAN and false positives on the old one. poll_tcp_connectivity() wraps iperf3 in a @Retry loop so the test waits until the expected reachability state is actually observed. It also accepts client_bind_dev and server_bind_dev to force iperf3 traffic through specific interfaces, bypassing ECMP when both secondaries share the same subnet. Signed-off-by: Asia Khromov <azhivovk@redhat.com> Assisted-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent 37a21c1 commit 9c9aaed

1 file changed

Lines changed: 40 additions & 0 deletions

File tree

tests/network/libs/connectivity.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import ipaddress
22
from typing import Final
33

4+
from timeout_sampler import TimeoutExpiredError, retry
5+
6+
from libs.net.traffic_generator import IPERF_SERVER_PORT, TcpServer, VMTcpClient
7+
from libs.vm.vm import BaseVirtualMachine
8+
49
ARP_ISOLATION_SYSCTL_CMD: Final[list[str]] = [
510
# Only answer ARP for the IP assigned to the receiving interface —
611
# prevents eth1 from responding to ARP for eth2's IP when queried from the same VLAN.
@@ -26,3 +31,38 @@ def build_ping_command(dst_ip: str, count: int, timeout: int) -> str:
2631
ip = ipaddress.ip_address(address=dst_ip)
2732
ping_ipv6_flag = " -6" if ip.version == 6 else ""
2833
return f"ping{ping_ipv6_flag} {dst_ip} -c {count} -w {timeout}"
34+
35+
36+
@retry(wait_timeout=60, sleep=5, exceptions_dict={})
37+
def poll_tcp_connectivity(
38+
client_vm: BaseVirtualMachine,
39+
server_vm: BaseVirtualMachine,
40+
server_ip: str,
41+
client_bind_dev: str | None = None,
42+
server_bind_dev: str | None = None,
43+
expect_connectivity: bool = True,
44+
) -> bool:
45+
"""Poll TCP connectivity (or its absence) between two VMs, retrying until the expected state is reached.
46+
47+
Args:
48+
client_vm: VM initiating the TCP connection.
49+
server_vm: VM running the iperf3 server.
50+
server_ip: IP address the server binds to.
51+
client_bind_dev: Guest network device name to force the client out (e.g. "eth1").
52+
Bypasses ECMP routing when both secondary interfaces share the same subnet.
53+
server_bind_dev: Guest network device name to force the server responses out (e.g. "eth1").
54+
Bypasses ECMP routing on the server VM when it has multiple secondary interfaces.
55+
expect_connectivity: When True polls until connectivity exists; when False polls until it does not.
56+
57+
Returns:
58+
True when the observed reachability matches expect_connectivity.
59+
"""
60+
try:
61+
with TcpServer(vm=server_vm, port=IPERF_SERVER_PORT, bind_ip=server_ip, bind_dev=server_bind_dev):
62+
with VMTcpClient(
63+
vm=client_vm, server_ip=server_ip, server_port=IPERF_SERVER_PORT, bind_dev=client_bind_dev
64+
):
65+
reachable = True
66+
except TimeoutExpiredError:
67+
reachable = False
68+
return reachable if expect_connectivity else not reachable

0 commit comments

Comments
 (0)