firecracker
172 строки · 5.6 Кб
1#!/usr/bin/env python3
2# Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3# SPDX-License-Identifier: Apache-2.0
4"""Script used to generate snapshots of microVMs."""
5
6import json7import os8import re9import shutil10import sys11from pathlib import Path12
13# Hack to be able to import testing framework functions.
14sys.path.append(os.path.join(os.getcwd(), "tests")) # noqa: E40215
16# pylint: disable=wrong-import-position
17from framework.artifacts import disks, kernels18from framework.microvm import MicroVMFactory19from framework.utils import generate_mmds_get_request, generate_mmds_session_token20from framework.utils_cpuid import CpuVendor, get_cpu_vendor21from host_tools.cargo_build import get_firecracker_binaries22
23# pylint: enable=wrong-import-position
24
25# Default IPv4 address to route MMDS requests.
26IPV4_ADDRESS = "169.254.169.254"27NET_IFACE_FOR_MMDS = "eth3"28# Path to the VM configuration file.
29VM_CONFIG_FILE = "tools/create_snapshot_artifact/complex_vm_config.json"30# Root directory for the snapshot artifacts.
31SNAPSHOT_ARTIFACTS_ROOT_DIR = "snapshot_artifacts"32
33
34def populate_mmds(microvm, data_store):35"""Populate MMDS contents with json data provided."""36# MMDS should be empty.37response = microvm.api.mmds.get()38assert response.json() == {}39
40# Populate MMDS with data.41microvm.api.mmds.put(**data_store)42
43# Ensure data is persistent inside the data store.44response = microvm.api.mmds.get()45assert response.json() == data_store46
47
48def validate_mmds(ssh_connection, data_store):49"""Validate that MMDS contents fetched from the guest."""50# Configure interface to route MMDS requests51cmd = "ip route add {} dev {}".format(IPV4_ADDRESS, NET_IFACE_FOR_MMDS)52_, stdout, stderr = ssh_connection.run(cmd)53assert stdout == stderr == ""54
55# Fetch metadata to ensure MMDS is accessible.56token = generate_mmds_session_token(ssh_connection, IPV4_ADDRESS, token_ttl=60)57
58cmd = generate_mmds_get_request(IPV4_ADDRESS, token=token)59_, stdout, _ = ssh_connection.run(cmd)60assert json.loads(stdout) == data_store61
62
63def main():64"""65Run the main logic.
66
67Create snapshot artifacts from complex microVMs with all Firecracker's
68functionality enabled. The kernels are parametrized to include all guest
69supported versions.
70
71Artifacts are saved in the following format:
72snapshot_artifacts
73|
74-> <guest_kernel_supported_0>_<cpu_template>_guest_snapshot
75|
76-> vm.mem
77-> vm.vmstate
78-> ubuntu-18.04.id_rsa
79-> ubuntu-18.04.ext4
80-> <guest_kernel_supported_1>_<cpu_template>_guest_snapshot
81|
82...
83"""
84# Create directory dedicated to store snapshot artifacts for85# each guest kernel version.86print("Cleanup")87shutil.rmtree(SNAPSHOT_ARTIFACTS_ROOT_DIR, ignore_errors=True)88vm_factory = MicroVMFactory(*get_firecracker_binaries())89
90cpu_templates = ["None"]91if get_cpu_vendor() == CpuVendor.INTEL:92cpu_templates.extend(["C3", "T2", "T2S"])93
94for cpu_template in cpu_templates:95for kernel in kernels(glob="vmlinux-*"):96for rootfs in disks(glob="ubuntu-*.squashfs"):97print(kernel, rootfs, cpu_template)98vm = vm_factory.build()99create_snapshots(vm, rootfs, kernel, cpu_template)100
101
102def create_snapshots(vm, rootfs, kernel, cpu_template):103"""Snapshot microVM built from vm configuration file."""104# Get ssh key from read-only artifact.105vm.ssh_key = rootfs.with_suffix(".id_rsa")106vm.rootfs_file = rootfs107vm.kernel_file = kernel108
109# adapt the JSON file110vm_config_file = Path(VM_CONFIG_FILE)111obj = json.load(vm_config_file.open(encoding="UTF-8"))112obj["boot-source"]["kernel_image_path"] = kernel.name113obj["drives"][0]["path_on_host"] = rootfs.name114obj["drives"][0]["is_read_only"] = True115obj["machine-config"]["cpu_template"] = cpu_template116vm.create_jailed_resource(vm_config_file)117vm_config = Path(vm.chroot()) / vm_config_file.name118vm_config.write_text(json.dumps(obj))119vm.jailer.extra_args = {"config-file": vm_config_file.name}120
121# since we are using a JSON file, we need to do this manually122vm.create_jailed_resource(rootfs)123vm.create_jailed_resource(kernel)124
125for i in range(4):126vm.add_net_iface(api=False)127
128vm.spawn(log_level="Info")129
130# Ensure the microVM has started.131assert vm.state == "Running"132
133# Populate MMDS.134data_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}144populate_mmds(vm, data_store)145
146# Iterate and validate connectivity on all ifaces after boot.147for i in range(4):148exit_code, _, _ = vm.ssh_iface(i).run("sync")149assert exit_code == 0150
151# Validate MMDS.152validate_mmds(vm.ssh, data_store)153
154# Snapshot the microVM.155snapshot = vm.snapshot_diff()156
157# Create snapshot artifacts directory specific for the kernel version used.158guest_kernel_version = re.search("vmlinux-(.*)", kernel.name)159
160snapshot_artifacts_dir = (161Path(SNAPSHOT_ARTIFACTS_ROOT_DIR)162/ f"{guest_kernel_version.group(1)}_{cpu_template}_guest_snapshot"163)164snapshot_artifacts_dir.mkdir(parents=True)165snapshot.save_to(snapshot_artifacts_dir)166print(f"Copied snapshot to: {snapshot_artifacts_dir}.")167
168vm.kill()169
170
171if __name__ == "__main__":172main()173