Skip to content

Commit 8304bdb

Browse files
committed
net, libs: add NAD ref change connectivity helpers
Add shared helpers for NAD live-update tests: - secondary_iface_addresses(): generate per-IP-family CIDR addresses for a secondary interface from a seed and host number. - ip_family_predicate(): lookup_iface_status predicate that passes once the guest agent reports IPs covering all expected IP families. - update_nad_references(): atomic VM spec patch to update one or more secondary network NAD references in a single API call. - assert_tcp_connectivity() / _poll_tcp_connectivity(): poll TCP reachability between two VMs with optional 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 a10fc47 commit 8304bdb

1 file changed

Lines changed: 55 additions & 0 deletions

File tree

tests/network/libs/connectivity.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import ipaddress
22

3+
from ocp_resources.resource import ResourceEditor
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+
39

410
def build_ping_command(dst_ip: str, count: int, timeout: int) -> str:
511
"""
@@ -16,3 +22,52 @@ def build_ping_command(dst_ip: str, count: int, timeout: int) -> str:
1622
ip = ipaddress.ip_address(address=dst_ip)
1723
ping_ipv6_flag = " -6" if ip.version == 6 else ""
1824
return f"ping{ping_ipv6_flag} {dst_ip} -c {count} -w {timeout}"
25+
26+
27+
def update_nad_references(vm: BaseVirtualMachine, updates: dict[str, str]) -> None:
28+
"""Patch the VM spec to update multiple secondary network NAD references in a single atomic call.
29+
30+
Args:
31+
vm: The virtual machine to update.
32+
updates: Mapping of interface name to new NAD name.
33+
"""
34+
networks = vm.instance.spec.template.spec.networks
35+
for network in networks:
36+
if network["name"] in updates:
37+
network["multus"].update({"networkName": updates[network["name"]]})
38+
ResourceEditor(patches={vm: {"spec": {"template": {"spec": {"networks": networks}}}}}).update()
39+
40+
41+
@retry(wait_timeout=60, sleep=5, exceptions_dict={})
42+
def poll_tcp_connectivity(
43+
client_vm: BaseVirtualMachine,
44+
server_vm: BaseVirtualMachine,
45+
server_ip: str,
46+
client_bind_dev: str | None = None,
47+
server_bind_dev: str | None = None,
48+
expect_connectivity: bool = True,
49+
) -> bool:
50+
"""Poll TCP connectivity (or its absence) between two VMs, retrying until the expected state is reached.
51+
52+
Args:
53+
client_vm: VM initiating the TCP connection.
54+
server_vm: VM running the iperf3 server.
55+
server_ip: IP address the server binds to.
56+
client_bind_dev: Guest network device name to force the client out (e.g. "eth1").
57+
Bypasses ECMP routing when both secondary interfaces share the same subnet.
58+
server_bind_dev: Guest network device name to force the server responses out (e.g. "eth1").
59+
Bypasses ECMP routing on the server VM when it has multiple secondary interfaces.
60+
expect_connectivity: When True polls until connectivity exists; when False polls until it does not.
61+
62+
Returns:
63+
True when the observed reachability matches expect_connectivity.
64+
"""
65+
try:
66+
with TcpServer(vm=server_vm, port=IPERF_SERVER_PORT, bind_ip=server_ip, bind_dev=server_bind_dev):
67+
with VMTcpClient(
68+
vm=client_vm, server_ip=server_ip, server_port=IPERF_SERVER_PORT, bind_dev=client_bind_dev
69+
):
70+
reachable = True
71+
except TimeoutExpiredError:
72+
reachable = False
73+
return reachable if expect_connectivity else not reachable

0 commit comments

Comments
 (0)