diff --git a/tests/framework/microvm.py b/tests/framework/microvm.py index c0eeb79ea60c..e3d5815ccfff 100644 --- a/tests/framework/microvm.py +++ b/tests/framework/microvm.py @@ -26,7 +26,7 @@ from enum import Enum from functools import lru_cache from pathlib import Path -from typing import Optional +from typing import Optional, Dict from tenacity import retry, stop_after_attempt, wait_fixed @@ -919,6 +919,7 @@ def restore_from_snapshot( snapshot: Snapshot, resume: bool = False, uffd_path: Path = None, + rename_interfaces: Dict[str, str] = None, ): """Restore a snapshot""" jailed_snapshot = snapshot.copy_to_chroot(Path(self.chroot())) @@ -941,11 +942,16 @@ def restore_from_snapshot( if uffd_path is not None: mem_backend = {"backend_type": "Uffd", "backend_path": str(uffd_path)} + iface_overrides = [] + if rename_interfaces: + iface_overrides = [{"iface_id": k, "host_dev_name": v} for k,v in rename_interfaces.items()] + self.api.snapshot_load.put( mem_backend=mem_backend, snapshot_path=str(jailed_vmstate), enable_diff_snapshots=snapshot.is_diff, resume_vm=resume, + network_overrides=iface_overrides, ) return jailed_snapshot @@ -986,13 +992,13 @@ def thread_backtraces(self): ) return "\n".join(backtraces) - def wait_for_up(self, timeout=10): + def wait_for_up(self, timeout=10, iface=0): """Wait for guest running inside the microVM to come up and respond. :param timeout: seconds to wait. """ try: - rc, stdout, stderr = self.ssh.run("true", timeout) + rc, stdout, stderr = self.ssh_iface(iface).run("true", timeout) except subprocess.TimeoutExpired: print( f"Remote command did not respond within {timeout}s\n\n" diff --git a/tests/integration_tests/functional/test_snapshot_basic.py b/tests/integration_tests/functional/test_snapshot_basic.py index 998d5d027af2..d8fd51bc746c 100644 --- a/tests/integration_tests/functional/test_snapshot_basic.py +++ b/tests/integration_tests/functional/test_snapshot_basic.py @@ -8,11 +8,13 @@ import re import shutil import time +import dataclasses from pathlib import Path import pytest import host_tools.drive as drive_tools +import host_tools.network as net_tools from framework.microvm import SnapshotType from framework.utils import check_filesystem, check_output from framework.utils_vsock import ( @@ -577,3 +579,30 @@ def test_vmgenid(guest_kernel_linux_6_1, rootfs, microvm_factory, snapshot_type) # Update the base for next iteration base_snapshot = snapshot + +def test_snapshot_rename_interface(uvm_nano, microvm_factory): + """ + Test that we can restore a snapshot and point its interface to a + different host interface. + """ + base_iface = net_tools.NetIfaceConfig.with_id(0) + + vm = uvm_nano + iface1 = dataclasses.replace(base_iface, tap_name="tap1") + vm.add_net_iface(iface=iface1) + # Create an interface but don't attach it to the device + vm.start() + vm.wait_for_up() + + snapshot = vm.snapshot_full() + + restored_vm = microvm_factory.build() + restored_vm.spawn() + iface2 = dataclasses.replace(base_iface, tap_name="tap2") + snapshot.net_ifaces.clear() + snapshot.net_ifaces.append(iface2) + restored_vm.restore_from_snapshot(snapshot, rename_interfaces= + {base_iface.dev_name: "tap2"}) + restored_vm.resume() + restored_vm.wait_for_up() +