__init__
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
"""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),
|
||||
)
|
||||
)
|
||||
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,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user