from __future__ import annotations import unittest from ipaddress import IPv4Address, IPv4Network from pathlib import Path from uvm.config import Settings from uvm.domain import VmRecord from uvm.firecracker.api import FirecrackerClient from uvm.firecracker.config import build_config, vcpu_count class RecordingFirecrackerClient(FirecrackerClient): def __init__(self) -> None: self.calls: list[tuple[str, str, object]] = [] def _request(self, method: str, path: str, body: object) -> None: self.calls.append((method, path, body)) class FirecrackerConfigTests(unittest.TestCase): def test_builds_a_writable_vm_disk_configuration(self) -> None: vm = VmRecord( id="vm-test", cpu=0.5, ram_mib=512, guest_ip="10.42.0.2", gateway="10.42.0.1", tap="uvm-test", mac="02:fc:00:00:00:01", socket="/tmp/firecracker.sock", config="/tmp/config.json", log="/tmp/firecracker.log", disk="/vm/rootfs.ext4", ) config = build_config( Settings(), vm, Path("/images/vmlinux"), Path("/vm/rootfs.ext4"), ) self.assertEqual(vcpu_count(0.5), 1) self.assertEqual(config["machine-config"]["mem_size_mib"], 512) self.assertEqual(config["drives"][0]["path_on_host"], "/vm/rootfs.ext4") self.assertIn("ip=10.42.0.2::10.42.0.1", config["boot-source"]["boot_args"]) self.assertNotIn("password", str(config)) def test_configures_firecracker_in_required_order_before_start(self) -> None: vm = VmRecord( id="vm-test", cpu=1, ram_mib=512, guest_ip="10.42.0.2", gateway="10.42.0.1", tap="uvm-test", mac="02:fc:00:00:00:01", socket="/tmp/firecracker.sock", config="/tmp/config.json", log="/tmp/firecracker.log", disk="/vm/rootfs.ext4", ) config = build_config( Settings(), vm, Path("/images/vmlinux"), Path("/vm/rootfs.ext4") ) client = RecordingFirecrackerClient() client.configure_and_start(config) self.assertEqual( [path for _method, path, _body in client.calls], [ "/machine-config", "/boot-source", "/drives/rootfs", "/network-interfaces/eth0", "/actions", ], ) self.assertEqual( client.calls[0][2], {"vcpu_count": 1, "mem_size_mib": 512, "smt": False}, ) self.assertEqual(client.calls[-1][2], {"action_type": "InstanceStart"}) def test_uses_the_configured_network_netmask_in_guest_boot_arguments(self) -> None: settings = Settings( network=IPv4Network("10.50.0.0/16"), gateway=IPv4Address("10.50.0.1"), ) vm = VmRecord( id="vm-test", cpu=1, ram_mib=512, guest_ip="10.50.0.2", gateway="10.50.0.1", tap="uvm-test", mac="02:fc:00:00:00:01", socket="/tmp/firecracker.sock", config="/tmp/config.json", log="/tmp/firecracker.log", ) config = build_config(settings, vm, Path("/images/vmlinux"), Path("/vm/rootfs.ext4")) self.assertIn("ip=10.50.0.2::10.50.0.1:255.255.0.0", config["boot-source"]["boot_args"])