Skip to content

Commit 5afad3e

Browse files
authored
[4.20] net, l2 bridge: Verify iface looping bug fix (RedHatQE#4406)
##### Short description: Manual cherry-pick: RedHatQE#4283 ##### More details: A [bug](https://issues.redhat.com/browse/CNV-79062) was causing secondary interface to disappear from VMI status. Validate that kubevirt/kubevirt#17041 had fixed the issue by enhancing the secondary interface stability test. ##### What this PR does / why we need it: ##### Which issue(s) this PR fixes: ##### Special notes for reviewer: ##### jira-ticket: https://redhat.atlassian.net/browse/CNV-83921 --------- Signed-off-by: Nir Dothan <ndothan@redhat.com>
1 parent 412a3d1 commit 5afad3e

File tree

5 files changed

+65
-24
lines changed

5 files changed

+65
-24
lines changed

libs/vm/vm.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from dataclasses import asdict
55
from typing import Any
66

7+
import yaml
78
from dacite import from_dict
89
from kubernetes.dynamic import DynamicClient
910
from ocp_resources.node import Node
@@ -12,6 +13,7 @@
1213
from pytest_testconfig import config as py_config
1314

1415
from libs.vm.spec import CloudInitNoCloud, ContainerDisk, Disk, SpecDisk, VMSpec, Volume
16+
from tests.network.libs import cloudinit
1517
from utilities import infra
1618
from utilities.constants import CLOUD_INIT_DISK_NAME
1719
from utilities.infra import get_nodes_cpu_architecture
@@ -88,6 +90,19 @@ def set_interface_state(self, network_name: str, state: str) -> None:
8890
}
8991
ResourceEditor(patches=patches).update()
9092

93+
@property
94+
def cloud_init_network_data(self) -> cloudinit.NetworkData:
95+
"""Return the parsed cloud-init network data configured for this VM.
96+
97+
Returns:
98+
NetworkData: The cloud-init network data as a dataclass.
99+
"""
100+
volumes = {vol.name: vol for vol in self.instance.spec.template.spec.volumes}
101+
return from_dict(
102+
data_class=cloudinit.NetworkData,
103+
data=yaml.safe_load(volumes[CLOUD_INIT_DISK_NAME].cloudInitNoCloud.networkData),
104+
)
105+
91106
@classmethod
92107
def from_existing(
93108
cls,

tests/network/l2_bridge/vmi_interfaces_stability/conftest.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@ def running_linux_bridge_vm(
3232
) as vm:
3333
vm.start(wait=True)
3434
vm.wait_for_agent_connected()
35-
wait_for_stable_ifaces(
36-
vm=vm,
37-
ipv4_supported_cluster=ipv4_supported_cluster,
38-
ipv6_supported_cluster=ipv6_supported_cluster,
39-
)
35+
wait_for_stable_ifaces(vm=vm)
4036
yield vm
4137

4238

tests/network/l2_bridge/vmi_interfaces_stability/lib_helpers.py

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
from libs.vm.spec import CloudInitNoCloud, Interface, Multus, Network
1414
from libs.vm.vm import BaseVirtualMachine, cloudinitdisk_storage
1515
from tests.network.libs import cloudinit
16+
from tests.network.localnet.liblocalnet import GUEST_1ST_IFACE_NAME, GUEST_3RD_IFACE_NAME
1617

1718
LOGGER = logging.getLogger(__name__)
1819

19-
LINUX_BRIDGE_IFACE_NAME: Final[str] = "linux-bridge"
20+
LINUX_BRIDGE_IFACE_NAME_1: Final[str] = "linux-bridge-1"
21+
LINUX_BRIDGE_IFACE_NAME_2: Final[str] = "linux-bridge-2"
2022

2123

2224
def secondary_network_vm(
@@ -29,12 +31,14 @@ def secondary_network_vm(
2931
) -> BaseVirtualMachine:
3032
spec = base_vmspec()
3133
spec.template.spec.domain.devices.interfaces = [ # type: ignore
34+
Interface(name=LINUX_BRIDGE_IFACE_NAME_1, bridge={}),
3235
Interface(name="default", masquerade={}),
33-
Interface(name=LINUX_BRIDGE_IFACE_NAME, bridge={}),
36+
Interface(name=LINUX_BRIDGE_IFACE_NAME_2, bridge={}),
3437
]
3538
spec.template.spec.networks = [
39+
Network(name=LINUX_BRIDGE_IFACE_NAME_1, multus=Multus(networkName=bridge_network_name)),
3640
Network(name="default", pod={}),
37-
Network(name=LINUX_BRIDGE_IFACE_NAME, multus=Multus(networkName=bridge_network_name)),
41+
Network(name=LINUX_BRIDGE_IFACE_NAME_2, multus=Multus(networkName=bridge_network_name)),
3842
]
3943

4044
ethernets = {}
@@ -43,11 +47,18 @@ def secondary_network_vm(
4347
ipv6_supported_cluster=ipv6_supported_cluster,
4448
)
4549
if primary:
46-
ethernets["eth0"] = primary
50+
ethernets["eth1"] = primary
4751

48-
ethernets["eth1"] = secondary_iface_cloud_init(
52+
ethernets["eth0"] = secondary_iface_cloud_init(
4953
ipv4_supported_cluster=ipv4_supported_cluster,
5054
ipv6_supported_cluster=ipv6_supported_cluster,
55+
host_address=1,
56+
)
57+
58+
ethernets["eth2"] = secondary_iface_cloud_init(
59+
ipv4_supported_cluster=ipv4_supported_cluster,
60+
ipv6_supported_cluster=ipv6_supported_cluster,
61+
host_address=2,
5162
)
5263

5364
userdata = cloudinit.UserData(users=[])
@@ -79,33 +90,46 @@ def primary_iface_cloud_init(
7990
def secondary_iface_cloud_init(
8091
ipv4_supported_cluster: bool,
8192
ipv6_supported_cluster: bool,
93+
host_address: int,
8294
) -> cloudinit.EthernetDevice:
8395
ips = secondary_iface_ips(
84-
ipv4_supported_cluster=ipv4_supported_cluster, ipv6_supported_cluster=ipv6_supported_cluster
96+
ipv4_supported_cluster=ipv4_supported_cluster,
97+
ipv6_supported_cluster=ipv6_supported_cluster,
98+
host_address=host_address,
8599
)
86100
addresses = [f"{ip}/64" if ipaddress.ip_address(ip).version == 6 else f"{ip}/24" for ip in ips]
87101
return cloudinit.EthernetDevice(addresses=addresses)
88102

89103

90-
def secondary_iface_ips(ipv4_supported_cluster: bool, ipv6_supported_cluster: bool) -> list[str]:
104+
def secondary_iface_ips(
105+
ipv4_supported_cluster: bool,
106+
ipv6_supported_cluster: bool,
107+
host_address: int,
108+
) -> list[str]:
91109
ips = []
92110
if ipv4_supported_cluster:
93-
ips.append(random_ipv4_address(net_seed=0, host_address=1))
111+
ips.append(random_ipv4_address(net_seed=0, host_address=host_address))
94112
if ipv6_supported_cluster:
95-
ips.append(random_ipv6_address(net_seed=0, host_address=1))
113+
ips.append(random_ipv6_address(net_seed=0, host_address=host_address))
96114
return ips
97115

98116

99117
def wait_for_stable_ifaces(
100118
vm: BaseVirtualMachine,
101-
ipv4_supported_cluster: bool,
102-
ipv6_supported_cluster: bool,
103119
) -> None:
104120
primary_network = lookup_primary_network(vm=vm)
105-
secondary_ips = secondary_iface_ips(
106-
ipv4_supported_cluster=ipv4_supported_cluster,
107-
ipv6_supported_cluster=ipv6_supported_cluster,
108-
)
121+
122+
secondary_iface_to_ips = {
123+
LINUX_BRIDGE_IFACE_NAME_1: [
124+
str(ipaddress.ip_interface(addr).ip)
125+
for addr in vm.cloud_init_network_data.ethernets[GUEST_1ST_IFACE_NAME].addresses # type: ignore[union-attr]
126+
],
127+
LINUX_BRIDGE_IFACE_NAME_2: [
128+
str(ipaddress.ip_interface(addr).ip)
129+
for addr in vm.cloud_init_network_data.ethernets[GUEST_3RD_IFACE_NAME].addresses # type: ignore[union-attr]
130+
],
131+
}
132+
109133
spec_interfaces = vm.instance.spec.template.spec.domain.devices.interfaces
110134
for iface in spec_interfaces:
111135
if iface.name == primary_network.name:
@@ -116,7 +140,7 @@ def wait_for_stable_ifaces(
116140
iface_name=iface.name,
117141
predicate=lambda iface_status: (
118142
"guest-agent" in iface_status["infoSource"]
119-
and all(ip in iface_status.get("ipAddresses", []) for ip in secondary_ips)
143+
and all(ip in iface_status.get("ipAddresses", []) for ip in secondary_iface_to_ips[iface.name])
120144
),
121145
)
122146

tests/network/l2_bridge/vmi_interfaces_stability/test_interfaces_stability.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ class TestInterfacesStability:
1616
@pytest.mark.polarion("CNV-14339")
1717
def test_interfaces_stability(self, running_linux_bridge_vm, stable_ips):
1818
for vmi_obj in monitor_vmi_events(vm=running_linux_bridge_vm, timeout=STABILITY_PERIOD_IN_SECONDS):
19-
assert_interfaces_stable(stable_ips=stable_ips, vmi=vmi_obj, expected_num_ifaces=2)
19+
assert_interfaces_stable(stable_ips=stable_ips, vmi=vmi_obj, expected_num_ifaces=len(stable_ips))
2020

2121
@pytest.mark.polarion("CNV-14340")
2222
def test_interfaces_stability_after_migration(self, running_linux_bridge_vm, stable_ips):
2323
migrate_vm_and_verify(vm=running_linux_bridge_vm)
2424
for vmi_obj in monitor_vmi_events(vm=running_linux_bridge_vm, timeout=STABILITY_PERIOD_IN_SECONDS):
25-
assert_interfaces_stable(stable_ips=stable_ips, vmi=vmi_obj, expected_num_ifaces=2)
25+
assert_interfaces_stable(stable_ips=stable_ips, vmi=vmi_obj, expected_num_ifaces=len(stable_ips))

tests/network/localnet/liblocalnet.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import contextlib
22
import logging
3-
from typing import Generator
3+
from typing import Final, Generator
44

55
from kubernetes.client import ApiException
66
from kubernetes.dynamic import DynamicClient
@@ -27,6 +27,12 @@
2727
LINK_STATE_UP = "up"
2828
LINK_STATE_DOWN = "down"
2929
_IPERF_SERVER_PORT = 5201
30+
NNCP_INTERFACE_TYPE_ETHERNET = "ethernet"
31+
GUEST_1ST_IFACE_NAME: Final[str] = "eth0"
32+
GUEST_2ND_IFACE_NAME: Final[str] = "eth1"
33+
GUEST_3RD_IFACE_NAME: Final[str] = "eth2"
34+
35+
3036
LOGGER = logging.getLogger(__name__)
3137

3238

0 commit comments

Comments
 (0)