Refactor VM lifecycle management and enhance validation

- Introduced a new method `installed_kernel` in `ImageStore` to handle kernel verification and checksum validation.
- Updated `Installer` to include additional packages: `nftables` and `conntrack`.
- Refined `LifecycleService` to improve error handling and state management during VM creation, starting, stopping, and deletion.
- Enhanced port management with new methods for setting and syncing ports, including validation for port rules.
- Improved error handling in `StateStore` to raise specific `NotFoundError` and `ConflictError` exceptions.
- Updated network management to ensure proper TAP device handling and firewall integration.
- Enhanced API responses in `vms.py` to include policy status for better client-side handling.
- Added validation for VM names and ports in `validation.py` to enforce stricter rules and prevent conflicts.
This commit is contained in:
2026-09-09 17:27:50 +05:30
parent 4ce14c3378
commit d8a25209b9
31 changed files with 9576 additions and 1331 deletions
+37 -9
View File
@@ -3,8 +3,9 @@
from __future__ import annotations
from ipaddress import IPv4Address
from typing import Literal
from pydantic import BaseModel, Field, SecretStr
from pydantic import BaseModel, ConfigDict, Field, SecretStr
from .domain import VmRecord
@@ -14,10 +15,9 @@ class HealthResponse(BaseModel):
class InstallRequest(BaseModel):
force: bool = False
model_config = ConfigDict(extra="forbid")
class Config:
extra = "forbid"
force: bool = False
class InstallResponse(BaseModel):
@@ -26,19 +26,31 @@ class InstallResponse(BaseModel):
rootfs: str
class PortRuleModel(BaseModel):
"""A private guest TCP port; HTTP and HTTPS are descriptive labels only."""
model_config = ConfigDict(extra="forbid")
port: int = Field(strict=True, ge=1, le=65535)
protocol: Literal["TCP", "HTTP", "HTTPS"] = "TCP"
class VmCreateRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
cpu: float = Field(default=1, gt=0)
ram: str | int | float = "512"
guest_ip: IPv4Address | None = None
username: str = "root"
password: SecretStr = SecretStr("root")
class Config:
extra = "forbid"
name: str | None = None
ports: list[PortRuleModel] | None = None
no_default_ports: bool = Field(default=False, strict=True)
class VmResponse(BaseModel):
id: str
name: str | None
cpu: float
ram_mib: int
guest_ip: str
@@ -47,6 +59,9 @@ class VmResponse(BaseModel):
username: str
status: str
observed_status: str
ports: list[PortRuleModel] | None
policy_status: str
operation: str | None
pid: int | None
created_at: int
updated_at: int
@@ -58,9 +73,15 @@ class DestroyResponse(BaseModel):
status: str = "terminated"
def vm_response(vm: VmRecord, *, observed_status: str | None = None) -> VmResponse:
def vm_response(
vm: VmRecord,
*,
observed_status: str | None = None,
policy_status: str = "unknown",
) -> VmResponse:
return VmResponse(
id=vm.id,
name=vm.name,
cpu=vm.cpu,
ram_mib=vm.ram_mib,
guest_ip=vm.guest_ip,
@@ -68,7 +89,14 @@ def vm_response(vm: VmRecord, *, observed_status: str | None = None) -> VmRespon
mac=vm.mac,
username=vm.username,
status=vm.status,
observed_status=observed_status or vm.status,
observed_status=observed_status if observed_status is not None else vm.status,
ports=(
[PortRuleModel(port=rule.port, protocol=rule.protocol) for rule in vm.ports]
if vm.ports is not None
else None
),
policy_status=policy_status,
operation=vm.operation,
pid=vm.pid,
created_at=vm.created_at,
updated_at=vm.updated_at,