diff --git a/libs/vm/vm.py b/libs/vm/vm.py index a93ce83bf9..bb9fef6833 100644 --- a/libs/vm/vm.py +++ b/libs/vm/vm.py @@ -10,6 +10,7 @@ from ocp_resources.virtual_machine import VirtualMachine from ocp_resources.virtual_machine_instance import VirtualMachineInstance from pytest_testconfig import config as py_config +from timeout_sampler import retry from libs.net.vmspec import VMInterfaceSpecNotFoundError from libs.vm.spec import ( @@ -130,7 +131,7 @@ def set_networks(self, networks: list[Network]) -> None: ResourceEditor(patches={self: {"spec": {"template": {"spec": {"networks": serialized}}}}}).update() def set_template_affinity(self, affinity: Affinity | None) -> None: - """Replace the VM template affinity. + """Replace the VM template affinity and wait for the VMI to reflect it. Serializes without dict_factory so that None-valued fields (e.g. podAffinity: None) are preserved as null in the merge patch, ensuring the old affinity type is removed. @@ -142,6 +143,15 @@ def set_template_affinity(self, affinity: Affinity | None) -> None: template_affinity = asdict(obj=affinity) if affinity else None patches = {self: {"spec": {"template": {"spec": {"affinity": template_affinity}}}}} ResourceEditor(patches=patches).update() + expected_affinity = asdict(obj=affinity, dict_factory=self._filter_out_none_values) if affinity else None + self._expected_vmi_affinity(expected_affinity=expected_affinity) + + @retry(wait_timeout=10, sleep=1) + def _expected_vmi_affinity(self, expected_affinity: dict[str, Any] | None) -> bool: + vmi_affinity = self.vmi.instance.to_dict()["spec"].get("affinity") + if vmi_affinity != expected_affinity: + raise ValueError(f"VMI {self.name} affinity mismatch: expected {expected_affinity}, got {vmi_affinity}") + return True @property def template_spec(self) -> VMISpec: diff --git a/tests/network/l2_bridge/migration_stuntime/test_migration_stuntime.py b/tests/network/l2_bridge/migration_stuntime/test_migration_stuntime.py index 8260985016..dcf0b76685 100644 --- a/tests/network/l2_bridge/migration_stuntime/test_migration_stuntime.py +++ b/tests/network/l2_bridge/migration_stuntime/test_migration_stuntime.py @@ -19,7 +19,12 @@ import pytest from libs.vm.affinity import new_pod_affinity, new_pod_anti_affinity -from tests.network.libs.stuntime import CLIENT_VM_LABEL, SERVER_VM_LABEL, STUNTIME_THRESHOLD_SECONDS, measure_stuntime +from tests.network.libs.stuntime import ( + CLIENT_VM_LABEL, + SERVER_VM_LABEL, + STUNTIME_THRESHOLD_SECONDS, + measure_stuntime, +) from utilities.virt import migrate_vm_and_verify pytestmark = [pytest.mark.tier3] @@ -71,6 +76,7 @@ def test_client_migrates_off_server_node( - Measured stuntime does not exceed the global threshold. """ stuntime_client_vm.set_template_affinity(affinity=new_pod_anti_affinity(label=SERVER_VM_LABEL)) + migrate_vm_and_verify(vm=stuntime_client_vm, client=admin_client) measured_stuntime = measure_stuntime(active_ping=l2_bridge_active_ping) assert measured_stuntime <= STUNTIME_THRESHOLD_SECONDS, ( @@ -130,6 +136,7 @@ def test_client_migrates_to_server_node( - Measured stuntime does not exceed the global threshold. """ stuntime_client_vm.set_template_affinity(affinity=new_pod_affinity(label=SERVER_VM_LABEL)) + migrate_vm_and_verify(vm=stuntime_client_vm, client=admin_client) measured_stuntime = measure_stuntime(active_ping=l2_bridge_active_ping) assert measured_stuntime <= STUNTIME_THRESHOLD_SECONDS, ( @@ -160,6 +167,7 @@ def test_server_migrates_off_client_node( - Measured stuntime does not exceed the global threshold. """ stuntime_server_vm.set_template_affinity(affinity=new_pod_anti_affinity(label=CLIENT_VM_LABEL)) + migrate_vm_and_verify(vm=stuntime_server_vm, client=admin_client) measured_stuntime = measure_stuntime(active_ping=l2_bridge_active_ping) assert measured_stuntime <= STUNTIME_THRESHOLD_SECONDS, ( @@ -189,7 +197,6 @@ def test_server_migrates_between_non_client_nodes( Expected: - Measured stuntime does not exceed the global threshold. """ - stuntime_server_vm.set_template_affinity(affinity=new_pod_anti_affinity(label=CLIENT_VM_LABEL)) migrate_vm_and_verify(vm=stuntime_server_vm, client=admin_client) measured_stuntime = measure_stuntime(active_ping=l2_bridge_active_ping) assert measured_stuntime <= STUNTIME_THRESHOLD_SECONDS, ( @@ -220,6 +227,7 @@ def test_server_migrates_to_client_node( - Measured stuntime does not exceed the global threshold. """ stuntime_server_vm.set_template_affinity(affinity=new_pod_affinity(label=CLIENT_VM_LABEL)) + migrate_vm_and_verify(vm=stuntime_server_vm, client=admin_client) measured_stuntime = measure_stuntime(active_ping=l2_bridge_active_ping) assert measured_stuntime <= STUNTIME_THRESHOLD_SECONDS, ( diff --git a/tests/network/localnet/migration_stuntime/test_migration_stuntime.py b/tests/network/localnet/migration_stuntime/test_migration_stuntime.py index 110d1a4f84..f3851401b9 100644 --- a/tests/network/localnet/migration_stuntime/test_migration_stuntime.py +++ b/tests/network/localnet/migration_stuntime/test_migration_stuntime.py @@ -76,6 +76,7 @@ def test_client_migrates_off_server_node(self, admin_client, ip_family, localnet - Measured stuntime does not exceed the global threshold. """ localnet_stuntime_client_vm.set_template_affinity(affinity=new_pod_anti_affinity(label=SERVER_VM_LABEL)) + migrate_vm_and_verify(vm=localnet_stuntime_client_vm, client=admin_client) measured_stuntime = measure_stuntime(active_ping=active_ping) assert measured_stuntime <= STUNTIME_THRESHOLD_SECONDS, ( @@ -133,6 +134,7 @@ def test_client_migrates_to_server_node(self, admin_client, ip_family, localnet_ - Measured stuntime does not exceed the global threshold. """ localnet_stuntime_client_vm.set_template_affinity(affinity=new_pod_affinity(label=SERVER_VM_LABEL)) + migrate_vm_and_verify(vm=localnet_stuntime_client_vm, client=admin_client) measured_stuntime = measure_stuntime(active_ping=active_ping) assert measured_stuntime <= STUNTIME_THRESHOLD_SECONDS, ( @@ -161,6 +163,7 @@ def test_server_migrates_off_client_node(self, admin_client, ip_family, localnet - Measured stuntime does not exceed the global threshold. """ localnet_stuntime_server_vm.set_template_affinity(affinity=new_pod_anti_affinity(label=CLIENT_VM_LABEL)) + migrate_vm_and_verify(vm=localnet_stuntime_server_vm, client=admin_client) measured_stuntime = measure_stuntime(active_ping=active_ping) assert measured_stuntime <= STUNTIME_THRESHOLD_SECONDS, ( @@ -190,7 +193,6 @@ def test_server_migrates_between_non_client_nodes( Expected: - Measured stuntime does not exceed the global threshold. """ - localnet_stuntime_server_vm.set_template_affinity(affinity=new_pod_anti_affinity(label=CLIENT_VM_LABEL)) migrate_vm_and_verify(vm=localnet_stuntime_server_vm, client=admin_client) measured_stuntime = measure_stuntime(active_ping=active_ping) assert measured_stuntime <= STUNTIME_THRESHOLD_SECONDS, ( @@ -219,6 +221,7 @@ def test_server_migrates_to_client_node(self, admin_client, ip_family, localnet_ - Measured stuntime does not exceed the global threshold. """ localnet_stuntime_server_vm.set_template_affinity(affinity=new_pod_affinity(label=CLIENT_VM_LABEL)) + migrate_vm_and_verify(vm=localnet_stuntime_server_vm, client=admin_client) measured_stuntime = measure_stuntime(active_ping=active_ping) assert measured_stuntime <= STUNTIME_THRESHOLD_SECONDS, ( diff --git a/utilities/virt.py b/utilities/virt.py index 937fdaf427..14a57be595 100644 --- a/utilities/virt.py +++ b/utilities/virt.py @@ -1952,6 +1952,7 @@ def migrate_vm_and_verify( wait_for_interfaces=wait_for_interfaces, check_ssh_connectivity=check_ssh_connectivity, ) + LOGGER.info(f"VMI {vm.vmi.name} migrated from {node_before.name} to {vm.vmi.node.name}.") return None