Files
nest/uvm/firecracker/config.py
T
kstyagi b0bf99a6ee feat: Add guest execution agent and vsock support
- Introduced a standalone guest execution agent to facilitate command execution within the guest VM over Firecracker's vsock.
- Enhanced FirecrackerClient to support vsock configuration, validating guest CID and UDS path.
- Updated configuration to include vsock settings by default.
- Refactored FirecrackerProcessManager to manage runtime sockets more effectively.
- Implemented guest agent installation and provisioning in the image store.
- Added API endpoints for executing commands in the VM and installing the guest agent.
- Improved error handling for guest execution failures and agent availability.
2026-09-25 17:39:06 +05:30

102 lines
2.8 KiB
Python

"""Build and persist Firecracker configuration for one VM."""
from __future__ import annotations
import json
import math
import os
import tempfile
from collections.abc import Mapping
from ipaddress import IPv4Address, IPv4Network
from pathlib import Path
from typing import Any
from ..config import Settings
from ..domain import VmRecord
def vcpu_count(cpu: float) -> int:
"""Firecracker accepts whole vCPUs; fractional capacity rounds up for now."""
return max(1, math.ceil(cpu))
def boot_ip_argument(
guest_ip: IPv4Address,
gateway: IPv4Address,
network: IPv4Network,
) -> str:
return f"ip={guest_ip}::{gateway}:{network.netmask}::eth0:off"
def build_config(
settings: Settings,
vm: VmRecord,
kernel: Path,
rootfs: Path,
) -> dict[str, Any]:
boot_args = " ".join(
(
"console=ttyS0",
"reboot=k",
"panic=1",
"pci=off",
boot_ip_argument(IPv4Address(vm.guest_ip), settings.gateway, settings.network),
)
)
if settings.architecture == "aarch64":
boot_args = f"keep_bootcon {boot_args}"
return {
"boot-source": {
"kernel_image_path": str(kernel),
"boot_args": boot_args,
},
"drives": [
{
"drive_id": "rootfs",
"path_on_host": str(rootfs),
"is_root_device": True,
"is_read_only": False,
}
],
"machine-config": {
"vcpu_count": vcpu_count(vm.cpu),
"mem_size_mib": vm.ram_mib,
},
"network-interfaces": [
{
"iface_id": "eth0",
"guest_mac": vm.mac,
"host_dev_name": vm.tap,
}
],
# Supply host-backed randomness before guest services such as sshd start.
"entropy": {},
# Each Firecracker instance has its own Unix-socket-backed vsock device;
# CIDs need not be allocated across these independent devices.
"vsock": {"guest_cid": 3, "uds_path": str(settings.guest_socket_path(vm.id))},
}
def write_config(path: Path, config: Mapping[str, Any]) -> None:
"""Atomically publish a rendered Firecracker configuration file."""
path.parent.mkdir(parents=True, exist_ok=True)
descriptor, temporary_name = tempfile.mkstemp(
prefix=f".{path.name}.",
suffix=".tmp",
dir=path.parent,
)
try:
with os.fdopen(descriptor, "w", encoding="utf-8") as temporary_file:
json.dump(config, temporary_file, indent=2)
temporary_file.write("\n")
temporary_file.flush()
os.fsync(temporary_file.fileno())
os.replace(temporary_name, path)
finally:
try:
os.unlink(temporary_name)
except FileNotFoundError:
pass