__init__
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
"""Typed Firecracker HTTP requests over a per-VM Unix-domain socket."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import http.client
|
||||
import json
|
||||
import socket
|
||||
from collections.abc import Mapping
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from ..errors import FirecrackerError
|
||||
|
||||
|
||||
class _UnixHTTPConnection(http.client.HTTPConnection):
|
||||
def __init__(self, socket_path: Path, timeout: float) -> None:
|
||||
super().__init__("localhost", timeout=timeout)
|
||||
self._socket_path = socket_path
|
||||
|
||||
def connect(self) -> None:
|
||||
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
self.sock.settimeout(self.timeout)
|
||||
self.sock.connect(str(self._socket_path))
|
||||
|
||||
|
||||
class FirecrackerClient:
|
||||
"""Keep Firecracker endpoint details out of lifecycle orchestration."""
|
||||
|
||||
def __init__(self, socket_path: Path, timeout_s: float) -> None:
|
||||
self._socket_path = socket_path
|
||||
self._timeout_s = timeout_s
|
||||
|
||||
def configure_and_start(self, config: Mapping[str, Any]) -> None:
|
||||
machine = config["machine-config"]
|
||||
boot = config["boot-source"]
|
||||
drives = config["drives"]
|
||||
interfaces = config["network-interfaces"]
|
||||
if not isinstance(machine, Mapping) or not isinstance(boot, Mapping):
|
||||
raise FirecrackerError("invalid Firecracker configuration")
|
||||
if not isinstance(drives, list) or not drives:
|
||||
raise FirecrackerError("Firecracker configuration has no root drive")
|
||||
if not isinstance(interfaces, list) or not interfaces:
|
||||
raise FirecrackerError("Firecracker configuration has no network interface")
|
||||
|
||||
self._request(
|
||||
"PUT",
|
||||
"/machine-config",
|
||||
{
|
||||
"vcpu_count": machine["vcpu_count"],
|
||||
"mem_size_mib": machine["mem_size_mib"],
|
||||
"smt": False,
|
||||
},
|
||||
)
|
||||
self._request("PUT", "/boot-source", boot)
|
||||
self._request("PUT", "/drives/rootfs", drives[0])
|
||||
self._request("PUT", "/network-interfaces/eth0", interfaces[0])
|
||||
self._request("PUT", "/actions", {"action_type": "InstanceStart"})
|
||||
|
||||
def _request(self, method: str, path: str, body: object) -> None:
|
||||
payload = json.dumps(body).encode("utf-8")
|
||||
connection = _UnixHTTPConnection(self._socket_path, self._timeout_s)
|
||||
try:
|
||||
connection.request(
|
||||
method,
|
||||
path,
|
||||
payload,
|
||||
{
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
)
|
||||
response = connection.getresponse()
|
||||
response_body = response.read().decode("utf-8", errors="replace")
|
||||
except (OSError, http.client.HTTPException) as error:
|
||||
raise FirecrackerError(f"Firecracker API {method} {path}: {error}") from error
|
||||
finally:
|
||||
connection.close()
|
||||
|
||||
if response.status >= 300:
|
||||
raise FirecrackerError(
|
||||
f"Firecracker API {method} {path}: {response.status} {response_body}"
|
||||
)
|
||||
Reference in New Issue
Block a user