firecracker

Форк
0
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

6
import json
7
import os
8
import re
9
import shutil
10
import sys
11
from pathlib import Path
12

13
# Hack to be able to import testing framework functions.
14
sys.path.append(os.path.join(os.getcwd(), "tests"))  # noqa: E402
15

16
# pylint: disable=wrong-import-position
17
from framework.artifacts import disks, kernels
18
from framework.microvm import MicroVMFactory
19
from framework.utils import generate_mmds_get_request, generate_mmds_session_token
20
from framework.utils_cpuid import CpuVendor, get_cpu_vendor
21
from host_tools.cargo_build import get_firecracker_binaries
22

23
# pylint: enable=wrong-import-position
24

25
# Default IPv4 address to route MMDS requests.
26
IPV4_ADDRESS = "169.254.169.254"
27
NET_IFACE_FOR_MMDS = "eth3"
28
# Path to the VM configuration file.
29
VM_CONFIG_FILE = "tools/create_snapshot_artifact/complex_vm_config.json"
30
# Root directory for the snapshot artifacts.
31
SNAPSHOT_ARTIFACTS_ROOT_DIR = "snapshot_artifacts"
32

33

34
def populate_mmds(microvm, data_store):
35
    """Populate MMDS contents with json data provided."""
36
    # MMDS should be empty.
37
    response = microvm.api.mmds.get()
38
    assert response.json() == {}
39

40
    # Populate MMDS with data.
41
    microvm.api.mmds.put(**data_store)
42

43
    # Ensure data is persistent inside the data store.
44
    response = microvm.api.mmds.get()
45
    assert response.json() == data_store
46

47

48
def validate_mmds(ssh_connection, data_store):
49
    """Validate that MMDS contents fetched from the guest."""
50
    # Configure interface to route MMDS requests
51
    cmd = "ip route add {} dev {}".format(IPV4_ADDRESS, NET_IFACE_FOR_MMDS)
52
    _, stdout, stderr = ssh_connection.run(cmd)
53
    assert stdout == stderr == ""
54

55
    # Fetch metadata to ensure MMDS is accessible.
56
    token = generate_mmds_session_token(ssh_connection, IPV4_ADDRESS, token_ttl=60)
57

58
    cmd = generate_mmds_get_request(IPV4_ADDRESS, token=token)
59
    _, stdout, _ = ssh_connection.run(cmd)
60
    assert json.loads(stdout) == data_store
61

62

63
def main():
64
    """
65
    Run the main logic.
66

67
    Create snapshot artifacts from complex microVMs with all Firecracker's
68
    functionality enabled. The kernels are parametrized to include all guest
69
    supported versions.
70

71
    Artifacts are saved in the following format:
72
    snapshot_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 for
85
    # each guest kernel version.
86
    print("Cleanup")
87
    shutil.rmtree(SNAPSHOT_ARTIFACTS_ROOT_DIR, ignore_errors=True)
88
    vm_factory = MicroVMFactory(*get_firecracker_binaries())
89

90
    cpu_templates = ["None"]
91
    if get_cpu_vendor() == CpuVendor.INTEL:
92
        cpu_templates.extend(["C3", "T2", "T2S"])
93

94
    for cpu_template in cpu_templates:
95
        for kernel in kernels(glob="vmlinux-*"):
96
            for rootfs in disks(glob="ubuntu-*.squashfs"):
97
                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()
169

170

171
if __name__ == "__main__":
172
    main()
173

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.