Skip to content

Commit b71a91f

Browse files
committed
fix: ensure snapshot_restore cross kernel test runs
Since PR#3896 this test always succeeded because it did not find any snapshot directories. Rewrite and fix the test so that - we see one test case for each directory, instead of one test per CPU template, so it is clear what is being restored. - switch to creating the microvm through the API rather than a JSON file so that the Microvm/Snapshot classes can account for all the devices. Signed-off-by: Pablo Barbáchano <[email protected]>
1 parent 8dbfbfd commit b71a91f

File tree

4 files changed

+110
-186
lines changed

4 files changed

+110
-186
lines changed

docs/snapshotting/snapshot-support.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -648,11 +648,8 @@ supported host kernel versions by generating snapshot artifacts through
648648
[this tool](../../tools/create_snapshot_artifact) and checking devices'
649649
functionality using
650650
[this test](../../tests/integration_tests/functional/test_snapshot_restore_cross_kernel.py).
651-
The microVM snapshotted is built from
652-
[this configuration file](../../tools/create_snapshot_artifact/complex_vm_config.json).
653-
The test restores the snapshot and ensures that all the devices set-up in the
654-
configuration file (network devices, disk, vsock, balloon and MMDS) are
655-
operational post-load.
651+
The test restores the snapshot and ensures that all the devices set-up (network
652+
devices, disk, vsock, balloon and MMDS) are operational post-load.
656653

657654
In those tests the instance is fixed, except some combinations where we also
658655
test across the same CPU family (Intel x86, Gravitons). In general cross-CPU

tests/integration_tests/functional/test_snapshot_restore_cross_kernel.py

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
make_guest_dirty_memory,
2424
)
2525

26+
pytestmark = pytest.mark.nonci
27+
2628

2729
def _test_balloon(microvm):
2830
# Get the firecracker pid.
@@ -65,17 +67,26 @@ def _test_mmds(vm, mmds_net_iface):
6567

6668
cmd = generate_mmds_get_request(mmds_ipv4_address, token=token)
6769
_, stdout, _ = vm.ssh.run(cmd)
68-
assert json.load(stdout) == data_store
70+
assert json.loads(stdout) == data_store
71+
72+
73+
def get_snapshot_dirs():
74+
"""Get all the snapshot directories"""
75+
snapshot_root_name = "snapshot_artifacts"
76+
snapshot_root_dir = Path(FC_WORKSPACE_DIR) / snapshot_root_name
77+
cpu_templates = ["C3", "T2", "T2S", "None"]
78+
if get_cpu_vendor() != CpuVendor.INTEL:
79+
cpu_templates = ["None"]
80+
for cpu_template in cpu_templates:
81+
for snapshot_dir in snapshot_root_dir.glob(f"*_{cpu_template}_guest_snapshot"):
82+
assert snapshot_dir.is_dir()
83+
yield pytest.param(snapshot_dir, id=snapshot_dir.name)
6984

7085

7186
@pytest.mark.timeout(600)
72-
@pytest.mark.nonci
73-
@pytest.mark.parametrize(
74-
"cpu_template",
75-
["C3", "T2", "T2S", "None"] if get_cpu_vendor() == CpuVendor.INTEL else ["None"],
76-
)
87+
@pytest.mark.parametrize("snapshot_dir", get_snapshot_dirs())
7788
def test_snap_restore_from_artifacts(
78-
microvm_factory, bin_vsock_path, test_fc_session_root_path, cpu_template
89+
microvm_factory, bin_vsock_path, test_fc_session_root_path, snapshot_dir
7990
):
8091
"""
8192
Restore from snapshots obtained with all supported guest kernel versions.
@@ -87,43 +98,37 @@ def test_snap_restore_from_artifacts(
8798
"""
8899
logger = logging.getLogger("cross_kernel_snapshot_restore")
89100

90-
snapshot_root_name = "snapshot_artifacts"
91-
snapshot_root_dir = Path(FC_WORKSPACE_DIR) / snapshot_root_name
92-
93101
# Iterate through all subdirectories based on CPU template
94102
# in the snapshot root dir.
95-
snap_subdirs = snapshot_root_dir.glob(f".*_{cpu_template}_guest_snapshot")
96-
for snapshot_dir in snap_subdirs:
97-
assert snapshot_dir.is_dir()
98-
logger.info("Working with snapshot artifacts in %s.", snapshot_dir)
103+
logger.info("Working with snapshot artifacts in %s.", snapshot_dir)
99104

100-
vm = microvm_factory.build()
101-
vm.spawn()
102-
logger.info("Loading microVM from snapshot...")
103-
vm.restore_from_path(snapshot_dir)
104-
vm.resume()
105+
vm = microvm_factory.build()
106+
vm.spawn()
107+
logger.info("Loading microVM from snapshot...")
108+
vm.restore_from_path(snapshot_dir)
109+
vm.resume()
105110

106-
# Ensure microVM is running.
107-
assert vm.state == "Running"
111+
# Ensure microVM is running.
112+
assert vm.state == "Running"
108113

109-
# Test that net devices have connectivity after restore.
110-
for idx, iface in enumerate(vm.iface.values()["iface"]):
111-
logger.info("Testing net device %s...", iface.dev_name)
112-
exit_code, _, _ = vm.ssh_iface(idx).run("sync")
113-
assert exit_code == 0
114+
# Test that net devices have connectivity after restore.
115+
for idx, iface in enumerate(vm.iface.values()):
116+
logger.info("Testing net device %s...", iface["iface"].dev_name)
117+
exit_code, _, _ = vm.ssh_iface(idx).run("true")
118+
assert exit_code == 0
114119

115-
logger.info("Testing data store behavior...")
116-
_test_mmds(vm, vm.iface["eth3"]["iface"])
120+
logger.info("Testing data store behavior...")
121+
_test_mmds(vm, vm.iface["eth3"]["iface"])
117122

118-
logger.info("Testing balloon device...")
119-
_test_balloon(vm)
123+
logger.info("Testing balloon device...")
124+
_test_balloon(vm)
120125

121-
logger.info("Testing vsock device...")
122-
check_vsock_device(vm, bin_vsock_path, test_fc_session_root_path, vm.ssh)
126+
logger.info("Testing vsock device...")
127+
check_vsock_device(vm, bin_vsock_path, test_fc_session_root_path, vm.ssh)
123128

124-
# Run fio on the guest.
125-
# TODO: check the result of FIO or use fsck to check that the root device is
126-
# not corrupted. No obvious errors will be returned here.
127-
guest_run_fio_iteration(vm.ssh, 0)
129+
# Run fio on the guest.
130+
# TODO: check the result of FIO or use fsck to check that the root device is
131+
# not corrupted. No obvious errors will be returned here.
132+
guest_run_fio_iteration(vm.ssh, 0)
128133

129-
vm.kill()
134+
vm.kill()

tools/create_snapshot_artifact/complex_vm_config.json

Lines changed: 0 additions & 70 deletions
This file was deleted.

tools/create_snapshot_artifact/main.py

Lines changed: 66 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,20 @@
1616
# pylint: disable=wrong-import-position
1717
from framework.artifacts import disks, kernels
1818
from framework.microvm import MicroVMFactory
19-
from framework.utils import generate_mmds_get_request, generate_mmds_session_token
19+
from framework.utils import (
20+
configure_mmds,
21+
generate_mmds_get_request,
22+
generate_mmds_session_token,
23+
)
2024
from framework.utils_cpuid import CpuVendor, get_cpu_vendor
2125
from host_tools.cargo_build import get_firecracker_binaries
2226

27+
2328
# pylint: enable=wrong-import-position
2429

2530
# Default IPv4 address to route MMDS requests.
2631
IPV4_ADDRESS = "169.254.169.254"
2732
NET_IFACE_FOR_MMDS = "eth3"
28-
# Path to the VM configuration file.
29-
VM_CONFIG_FILE = "tools/create_snapshot_artifact/complex_vm_config.json"
3033
# Root directory for the snapshot artifacts.
3134
SNAPSHOT_ARTIFACTS_ROOT_DIR = "snapshot_artifacts"
3235

@@ -95,77 +98,66 @@ def main():
9598
for kernel in kernels(glob="vmlinux-*"):
9699
for rootfs in disks(glob="ubuntu-*.squashfs"):
97100
print(kernel, rootfs, cpu_template)
98-
vm = vm_factory.build()
99-
create_snapshots(vm, rootfs, kernel, cpu_template)
100-
101-
102-
def create_snapshots(vm, rootfs, kernel, cpu_template):
103-
"""Snapshot microVM built from vm configuration file."""
104-
# Get ssh key from read-only artifact.
105-
vm.ssh_key = rootfs.with_suffix(".id_rsa")
106-
vm.rootfs_file = rootfs
107-
vm.kernel_file = kernel
108-
109-
# adapt the JSON file
110-
vm_config_file = Path(VM_CONFIG_FILE)
111-
obj = json.load(vm_config_file.open(encoding="UTF-8"))
112-
obj["boot-source"]["kernel_image_path"] = kernel.name
113-
obj["drives"][0]["path_on_host"] = rootfs.name
114-
obj["drives"][0]["is_read_only"] = True
115-
obj["machine-config"]["cpu_template"] = cpu_template
116-
vm.create_jailed_resource(vm_config_file)
117-
vm_config = Path(vm.chroot()) / vm_config_file.name
118-
vm_config.write_text(json.dumps(obj))
119-
vm.jailer.extra_args = {"config-file": vm_config_file.name}
120-
121-
# since we are using a JSON file, we need to do this manually
122-
vm.create_jailed_resource(rootfs)
123-
vm.create_jailed_resource(kernel)
124-
125-
for i in range(4):
126-
vm.add_net_iface(api=False)
127-
128-
vm.spawn(log_level="Info")
129-
130-
# Ensure the microVM has started.
131-
assert vm.state == "Running"
132-
133-
# Populate MMDS.
134-
data_store = {
135-
"latest": {
136-
"meta-data": {
137-
"ami-id": "ami-12345678",
138-
"reservation-id": "r-fea54097",
139-
"local-hostname": "ip-10-251-50-12.ec2.internal",
140-
"public-hostname": "ec2-203-0-113-25.compute-1.amazonaws.com",
141-
}
142-
}
143-
}
144-
populate_mmds(vm, data_store)
145-
146-
# Iterate and validate connectivity on all ifaces after boot.
147-
for i in range(4):
148-
exit_code, _, _ = vm.ssh_iface(i).run("sync")
149-
assert exit_code == 0
150-
151-
# Validate MMDS.
152-
validate_mmds(vm.ssh, data_store)
153-
154-
# Snapshot the microVM.
155-
snapshot = vm.snapshot_diff()
156-
157-
# Create snapshot artifacts directory specific for the kernel version used.
158-
guest_kernel_version = re.search("vmlinux-(.*)", kernel.name)
159-
160-
snapshot_artifacts_dir = (
161-
Path(SNAPSHOT_ARTIFACTS_ROOT_DIR)
162-
/ f"{guest_kernel_version.group(1)}_{cpu_template}_guest_snapshot"
163-
)
164-
snapshot_artifacts_dir.mkdir(parents=True)
165-
snapshot.save_to(snapshot_artifacts_dir)
166-
print(f"Copied snapshot to: {snapshot_artifacts_dir}.")
167-
168-
vm.kill()
101+
vm = vm_factory.build(kernel, rootfs)
102+
vm.spawn(log_level="Info")
103+
vm.basic_config(
104+
vcpu_count=2,
105+
mem_size_mib=1024,
106+
cpu_template=cpu_template,
107+
track_dirty_pages=True,
108+
)
109+
# Add 4 network devices
110+
for i in range(4):
111+
vm.add_net_iface()
112+
# Add a vsock device
113+
vm.api.vsock.put(vsock_id="vsock0", guest_cid=3, uds_path="/v.sock")
114+
# Add MMDS
115+
configure_mmds(vm, ["eth3"], version="V2")
116+
# Add a memory balloon.
117+
vm.api.balloon.put(
118+
amount_mib=0, deflate_on_oom=True, stats_polling_interval_s=1
119+
)
120+
121+
vm.start()
122+
# Ensure the microVM has started.
123+
assert vm.state == "Running"
124+
125+
# Populate MMDS.
126+
data_store = {
127+
"latest": {
128+
"meta-data": {
129+
"ami-id": "ami-12345678",
130+
"reservation-id": "r-fea54097",
131+
"local-hostname": "ip-10-251-50-12.ec2.internal",
132+
"public-hostname": "ec2-203-0-113-25.compute-1.amazonaws.com",
133+
}
134+
}
135+
}
136+
populate_mmds(vm, data_store)
137+
138+
# Iterate and validate connectivity on all ifaces after boot.
139+
for i in range(4):
140+
exit_code, _, _ = vm.ssh_iface(i).run("sync")
141+
assert exit_code == 0
142+
143+
# Validate MMDS.
144+
validate_mmds(vm.ssh, data_store)
145+
146+
# Snapshot the microVM.
147+
snapshot = vm.snapshot_diff()
148+
149+
# Create snapshot artifacts directory specific for the kernel version used.
150+
guest_kernel_version = re.search("vmlinux-(.*)", kernel.name)
151+
152+
snapshot_artifacts_dir = (
153+
Path(SNAPSHOT_ARTIFACTS_ROOT_DIR)
154+
/ f"{guest_kernel_version.group(1)}_{cpu_template}_guest_snapshot"
155+
)
156+
snapshot_artifacts_dir.mkdir(parents=True)
157+
snapshot.save_to(snapshot_artifacts_dir)
158+
print(f"Copied snapshot to: {snapshot_artifacts_dir}.")
159+
160+
vm.kill()
169161

170162

171163
if __name__ == "__main__":

0 commit comments

Comments
 (0)