Files

193 lines
5.5 KiB
Python

"""
Sovereign Orchestrator - Pydantic Models
Data models for API request/response payloads and internal state.
"""
from __future__ import annotations
import uuid
from datetime import datetime, timezone
from enum import Enum
from typing import Optional
from pydantic import BaseModel, Field
# ---------------------------------------------------------------------------
# Enums
# ---------------------------------------------------------------------------
class BuildState(str, Enum):
"""Lifecycle states for an ISO build job."""
PENDING = "pending"
BUILDING = "building"
UPLOADING = "uploading"
COMPLETED = "completed"
FAILED = "failed"
class NetworkSource(str, Enum):
"""Proxmox installer network source options."""
FROM_DHCP = "from-dhcp"
FROM_ANSWER = "from-answer"
class Filesystem(str, Enum):
"""Supported root filesystems."""
EXT4 = "ext4"
XFS = "xfs"
ZFS = "zfs"
BTRFS = "btrfs"
# ---------------------------------------------------------------------------
# Request models
# ---------------------------------------------------------------------------
class ISOConfig(BaseModel):
"""Configuration payload for generating a custom auto-install ISO."""
fqdn: str = Field(
..., description="Fully qualified domain name for the new host",
examples=["pve-test.internal.718it.biz"],
)
keyboard: str = Field(default="en-us", description="Keyboard layout")
country: str = Field(default="us", description="Country code")
timezone: str = Field(
default="America/New_York", description="Timezone string"
)
mailto: str = Field(
default="wmantly@gmail.com", description="Admin email address"
)
root_password: str = Field(
..., description="Root password for the installed system"
)
root_ssh_keys: list[str] = Field(
default_factory=list,
description="SSH public keys to authorize for root",
)
network_source: NetworkSource = Field(
default=NetworkSource.FROM_DHCP,
description="Network configuration source",
)
filesystem: Filesystem = Field(
default=Filesystem.EXT4, description="Root filesystem type"
)
disk_list: list[str] = Field(
default_factory=lambda: ["sda"],
description="Disks to use for installation",
)
reboot_on_error: bool = Field(
default=False, description="Reboot automatically on install error"
)
class DeployConfig(BaseModel):
"""Configuration for deploying a VM with the custom ISO."""
vmid: int = Field(default=900, description="VM ID to create")
node: str = Field(default="dl380-0", description="Proxmox node name")
cores: int = Field(default=4, ge=1, le=128, description="CPU cores")
memory: int = Field(
default=8192, ge=512, description="Memory in MiB"
)
disk_size: str = Field(
default="128G", description="Root disk size (e.g. 128G)"
)
build_id: Optional[str] = Field(
default=None,
description="Build ID whose ISO to use. Defaults to latest.",
)
storage: str = Field(
default="local-lvm", description="Proxmox storage for the VM disk"
)
iso_storage: str = Field(
default="local", description="Proxmox storage containing ISOs"
)
bridge: str = Field(
default="vmbr1", description="Network bridge to attach the VM to"
)
# ---------------------------------------------------------------------------
# Internal / response models
# ---------------------------------------------------------------------------
class BuildStatus(BaseModel):
"""Tracks the state of an ISO build job."""
id: str = Field(default_factory=lambda: uuid.uuid4().hex[:12])
state: BuildState = BuildState.PENDING
iso_config: Optional[ISOConfig] = None
iso_filename: Optional[str] = None
logs: list[str] = Field(default_factory=list)
created_at: datetime = Field(
default_factory=lambda: datetime.now(timezone.utc)
)
updated_at: datetime = Field(
default_factory=lambda: datetime.now(timezone.utc)
)
error: Optional[str] = None
def log(self, message: str) -> None:
"""Append a timestamped log line."""
ts = datetime.now(timezone.utc).isoformat()
self.logs.append(f"[{ts}] {message}")
self.updated_at = datetime.now(timezone.utc)
class VMStatus(BaseModel):
"""Proxmox VM status snapshot."""
vmid: int
name: Optional[str] = None
status: str
node: str
cpus: Optional[int] = None
maxmem: Optional[int] = None
maxdisk: Optional[int] = None
uptime: Optional[int] = None
pid: Optional[int] = None
class NodeInfo(BaseModel):
"""Proxmox node summary."""
node: str
status: str
cpu: Optional[float] = None
maxcpu: Optional[int] = None
mem: Optional[int] = None
maxmem: Optional[int] = None
uptime: Optional[int] = None
class ISOInfo(BaseModel):
"""ISO file metadata."""
volid: str
filename: str
size: Optional[int] = None
class SystemStatus(BaseModel):
"""Aggregate system status returned by /api/status."""
nodes: list[NodeInfo] = Field(default_factory=list)
test_vm: Optional[VMStatus] = None
available_isos: list[ISOInfo] = Field(default_factory=list)
active_builds: list[BuildStatus] = Field(default_factory=list)
proxmox_connected: bool = False
error: Optional[str] = None
class DeployResult(BaseModel):
"""Response from a deploy operation."""
vmid: int
node: str
status: str
message: str
task_id: Optional[str] = None