Path: blob/main/tests/integration_tests/functional/test_snapshot_advanced.py
1958 views
# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.1# SPDX-License-Identifier: Apache-2.02"""Advanced tests scenarios for snapshot save/restore."""34import logging5import platform6import tempfile7import pytest8from test_balloon import _test_rss_memory_lower, copy_util_to_rootfs9from conftest import _test_images_s3_bucket10from framework.artifacts import ArtifactCollection, NetIfaceConfig11from framework.builder import MicrovmBuilder, SnapshotBuilder, SnapshotType12from framework.utils import get_firecracker_version_from_toml13import host_tools.network as net_tools # pylint: disable=import-error14import host_tools.drive as drive_tools151617# Define 4 net device configurations.18net_ifaces = [NetIfaceConfig(),19NetIfaceConfig(host_ip="192.168.1.1",20guest_ip="192.168.1.2",21tap_name="tap1",22dev_name="eth1"),23NetIfaceConfig(host_ip="192.168.2.1",24guest_ip="192.168.2.2",25tap_name="tap2",26dev_name="eth2"),27NetIfaceConfig(host_ip="192.168.3.1",28guest_ip="192.168.3.2",29tap_name="tap3",30dev_name="eth3")]31# Define 4 scratch drives.32scratch_drives = ["vdb", "vdc", "vdd", "vde", "vdf"]333435@pytest.mark.skipif(36platform.machine() != "x86_64",37reason="Not supported yet."38)39def test_restore_old_snapshot_all_devices(bin_cloner_path):40"""Test scenario: restore previous version snapshots in current version."""41# Microvm: 2vCPU 256MB RAM, balloon, 4 disks and 4 net devices.42logger = logging.getLogger("old_snapshot_many_devices")43builder = MicrovmBuilder(bin_cloner_path)4445artifacts = ArtifactCollection(_test_images_s3_bucket())46# Fetch all firecracker binaries.47# With each binary create a snapshot and try to restore in current48# version.49firecracker_artifacts = artifacts.firecrackers(50older_than=get_firecracker_version_from_toml())51for firecracker in firecracker_artifacts:52firecracker.download()53jailer = firecracker.jailer()54jailer.download()5556logger.info("Creating snapshot with Firecracker: %s",57firecracker.local_path())58logger.info("Using Jailer: %s", jailer.local_path())5960target_version = firecracker.base_name()[1:]6162# v0.23 does not support creating diff snapshots.63# v0.23 does not support balloon.64diff_snapshots = "0.23" not in target_version6566# Create a snapshot.67snapshot = create_snapshot_helper(builder,68logger,69drives=scratch_drives,70ifaces=net_ifaces,71fc_binary=firecracker.local_path(),72jailer_binary=jailer.local_path(),73diff_snapshots=diff_snapshots,74balloon=diff_snapshots)7576# Resume microvm using current build of FC/Jailer.77microvm, _ = builder.build_from_snapshot(snapshot,78resume=True,79diff_snapshots=False)80validate_all_devices(logger, microvm, net_ifaces, scratch_drives,81diff_snapshots)82logger.debug("========== Firecracker restore snapshot log ==========")83logger.debug(microvm.log_data)848586@pytest.mark.skipif(87platform.machine() != "x86_64",88reason="Not supported yet."89)90def test_restore_old_version_all_devices(bin_cloner_path):91"""Test scenario: restore snapshot in previous versions of Firecracker."""92# Microvm: 2vCPU 256MB RAM, balloon, 4 disks and 4 net devices.93logger = logging.getLogger("old_snapshot_version_many_devices")94builder = MicrovmBuilder(bin_cloner_path)9596artifacts = ArtifactCollection(_test_images_s3_bucket())97# Fetch all firecracker binaries.98# Create a snapshot with current build and restore with each FC binary99# artifact.100firecracker_artifacts = artifacts.firecrackers(101older_than=get_firecracker_version_from_toml())102for firecracker in firecracker_artifacts:103firecracker.download()104jailer = firecracker.jailer()105jailer.download()106107logger.info("Creating snapshot with local build")108109# Old version from artifact.110target_version = firecracker.base_name()[1:]111# v0.23 does not have a balloon device.112balloon = "0.23" not in target_version113114# Create a snapshot with current FC version targeting the old version.115snapshot = create_snapshot_helper(builder,116logger,117target_version=target_version,118drives=scratch_drives,119ifaces=net_ifaces,120balloon=balloon,121diff_snapshots=True)122123logger.info("Restoring snapshot with Firecracker: %s",124firecracker.local_path())125logger.info("Using Jailer: %s", jailer.local_path())126127# Resume microvm using FC/Jailer binary artifacts.128vm, _ = builder.build_from_snapshot(snapshot,129resume=True,130diff_snapshots=False,131fc_binary=firecracker.local_path(),132jailer_binary=jailer.local_path())133validate_all_devices(logger, vm, net_ifaces, scratch_drives,134balloon)135logger.debug("========== Firecracker restore snapshot log ==========")136logger.debug(vm.log_data)137138139@pytest.mark.skipif(140platform.machine() != "x86_64",141reason="TSC is x86_64 specific."142)143def test_restore_no_tsc(bin_cloner_path):144"""Test scenario: restore a snapshot without TSC in current version."""145logger = logging.getLogger("no_tsc_snapshot")146builder = MicrovmBuilder(bin_cloner_path)147148artifacts = ArtifactCollection(_test_images_s3_bucket())149# Fetch the v0.24.0 firecracker binary as that one does not have150# the TSC frequency in the snapshot file.151firecracker_artifacts = artifacts.firecrackers(152keyword="v0.24.0"153)154firecracker = firecracker_artifacts[0]155firecracker.download()156jailer = firecracker.jailer()157jailer.download()158diff_snapshots = True159160# Create a snapshot.161snapshot = create_snapshot_helper(162builder,163logger,164drives=scratch_drives,165ifaces=net_ifaces,166fc_binary=firecracker.local_path(),167jailer_binary=jailer.local_path(),168diff_snapshots=diff_snapshots,169balloon=True170)171172# Resume microvm using current build of FC/Jailer.173# The resume should be successful because the CPU model174# in the snapshot state is the same as this host's.175microvm, _ = builder.build_from_snapshot(176snapshot,177resume=True,178diff_snapshots=False179)180validate_all_devices(181logger,182microvm,183net_ifaces,184scratch_drives,185diff_snapshots186)187logger.debug("========== Firecracker restore snapshot log ==========")188logger.debug(microvm.log_data)189190191@pytest.mark.skipif(192platform.machine() != "x86_64",193reason="TSC is x86_64 specific."194)195def test_save_tsc_old_version(bin_cloner_path):196"""Test TSC warning message when saving old snapshot."""197vm_builder = MicrovmBuilder(bin_cloner_path)198vm_instance = vm_builder.build_vm_nano()199vm = vm_instance.vm200201vm.start()202203vm.pause_to_snapshot(204mem_file_path='memfile',205snapshot_path='statefile',206diff=False,207version='0.24.0'208)209210log_data = vm.log_data211assert "Saving to older snapshot version, TSC freq" in log_data212vm.kill()213214215def validate_all_devices(216logger,217microvm,218ifaces,219drives,220balloon221):222"""Perform a basic validation for all devices of a microvm."""223# Test that net devices have connectivity after restore.224for iface in ifaces:225logger.info("Testing net device %s", iface.dev_name)226microvm.ssh_config['hostname'] = iface.guest_ip227ssh_connection = net_tools.SSHConnection(microvm.ssh_config)228exit_code, _, _ = ssh_connection.execute_command("sync")229230# Drop page cache.231# Ensure further reads are going to be served from emulation layer.232cmd = "sync; echo 1 > /proc/sys/vm/drop_caches"233exit_code, _, _ = ssh_connection.execute_command(cmd)234assert exit_code == 0235236# Validate checksum of /dev/vdX/test.237# Should be ab893875d697a3145af5eed5309bee26 for 10 pages238# of zeroes.239for drive in drives:240# Mount block device.241logger.info("Testing drive %s", drive)242cmd = "mount /dev/{drive} /mnt/{drive}".format(drive=drive)243exit_code, _, _ = ssh_connection.execute_command(cmd)244assert exit_code == 0245246# Validate checksum.247cmd = "md5sum /mnt/{}/test | cut -d ' ' -f 1".format(drive)248exit_code, stdout, _ = ssh_connection.execute_command(cmd)249assert exit_code == 0250assert stdout.read().strip() == "ab893875d697a3145af5eed5309bee26"251logger.info("* checksum OK.")252253if balloon is True:254logger.info("Testing balloon memory reclaim.")255# Call helper fn from balloon integration tests.256_test_rss_memory_lower(microvm)257258259def create_snapshot_helper(builder, logger, target_version=None,260drives=None, ifaces=None,261balloon=False, diff_snapshots=False,262fc_binary=None, jailer_binary=None):263"""Create a snapshot with many devices."""264vm_instance = builder.build_vm_nano(net_ifaces=ifaces,265diff_snapshots=diff_snapshots,266fc_binary=fc_binary,267jailer_binary=jailer_binary)268vm = vm_instance.vm269270if diff_snapshots is False:271snapshot_type = SnapshotType.FULL272else:273# Version 0.24 and greater has Diff and balloon support.274snapshot_type = SnapshotType.DIFF275276if balloon:277# Copy balloon test util.278copy_util_to_rootfs(vm_instance.disks[0].local_path(), 'fillmem')279280# Add a memory balloon with stats enabled.281response = vm.balloon.put(282amount_mib=0,283deflate_on_oom=True,284stats_polling_interval_s=1285)286assert vm.api_session.is_status_no_content(response.status_code)287288# Disk path array needed when creating the snapshot later.289disks = [vm_instance.disks[0].local_path()]290test_drives = [] if drives is None else drives291292# Add disks.293for scratch in test_drives:294# Add a scratch 64MB RW non-root block device.295scratchdisk = drive_tools.FilesystemFile(tempfile.mktemp(), size=64)296vm.add_drive(scratch, scratchdisk.path)297disks.append(scratchdisk.path)298299# Workaround FilesystemFile destructor removal of file.300scratchdisk.path = None301302vm.start()303304# Iterate and validate connectivity on all ifaces after boot.305for iface in net_ifaces:306vm.ssh_config['hostname'] = iface.guest_ip307ssh_connection = net_tools.SSHConnection(vm.ssh_config)308exit_code, _, _ = ssh_connection.execute_command("sync")309assert exit_code == 0310311# Mount scratch drives in guest.312for blk in scratch_drives:313# Create mount point and mount each device.314cmd = "mkdir -p /mnt/{blk} && mount /dev/{blk} /mnt/{blk}".format(315blk=blk316)317exit_code, _, _ = ssh_connection.execute_command(cmd)318assert exit_code == 0319320# Create file using dd using O_DIRECT.321# After resume we will compute md5sum on these files.322dd = "dd if=/dev/zero of=/mnt/{}/test bs=4096 count=10 oflag=direct"323exit_code, _, _ = ssh_connection.execute_command(dd.format(blk))324assert exit_code == 0325326# Unmount the device.327cmd = "umount /dev/{}".format(blk)328exit_code, _, _ = ssh_connection.execute_command(cmd)329assert exit_code == 0330331# Create a snapshot builder from a microvm.332snapshot_builder = SnapshotBuilder(vm)333334snapshot = snapshot_builder.create(disks,335vm_instance.ssh_key,336target_version=target_version,337snapshot_type=snapshot_type,338net_ifaces=net_ifaces)339logger.debug("========== Firecracker create snapshot log ==========")340logger.debug(vm.log_data)341vm.kill()342return snapshot343344345