"""Pydantic schemas for Docker management.""" from datetime import datetime from typing import Optional, List, Dict, Any, Literal from pydantic import BaseModel, Field, ConfigDict # === Container Schemas === class ContainerCustomizationOut(BaseModel): id: int host_id: str container_id: str icon_key: Optional[str] = None icon_color: Optional[str] = None bg_color: Optional[str] = None updated_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class ContainerCustomizationUpsert(BaseModel): icon_key: Optional[str] = None icon_color: Optional[str] = None bg_color: Optional[str] = None class ContainerCustomizationListResponse(BaseModel): customizations: List[ContainerCustomizationOut] class DockerContainerBase(BaseModel): """Base schema for Docker container.""" container_id: str = Field(..., description="Docker container ID") name: str = Field(..., description="Container name") image: Optional[str] = Field(None, description="Image name") state: Literal["running", "exited", "paused", "created", "dead", "restarting", "removing", "unknown"] = Field( "unknown", description="Container state" ) status: Optional[str] = Field(None, description="Container status string") health: Optional[Literal["healthy", "unhealthy", "starting", "none"]] = Field( None, description="Health check status" ) ports: Optional[Dict[str, Any]] = Field(None, description="Port mappings") labels: Optional[Dict[str, str]] = Field(None, description="Container labels") compose_project: Optional[str] = Field(None, description="Docker Compose project name") class DockerContainerResponse(DockerContainerBase): """Response schema for Docker container.""" id: int host_id: str customization: Optional[ContainerCustomizationOut] = None created_at: Optional[datetime] = None last_update_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class DockerContainerListResponse(BaseModel): """Response for listing containers.""" containers: List[DockerContainerResponse] total: int running: int stopped: int # === Image Schemas === class DockerImageBase(BaseModel): """Base schema for Docker image.""" image_id: str = Field(..., description="Docker image ID") repo_tags: Optional[List[str]] = Field(None, description="Repository tags") size: Optional[int] = Field(None, description="Image size in bytes") class DockerImageResponse(DockerImageBase): """Response schema for Docker image.""" id: int host_id: str created: Optional[datetime] = None last_update_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class DockerImageListResponse(BaseModel): """Response for listing images.""" images: List[DockerImageResponse] total: int total_size: int # === Volume Schemas === class DockerVolumeBase(BaseModel): """Base schema for Docker volume.""" name: str = Field(..., description="Volume name") driver: Optional[str] = Field(None, description="Volume driver") mountpoint: Optional[str] = Field(None, description="Mount point path") scope: Optional[str] = Field(None, description="Volume scope") class DockerVolumeResponse(DockerVolumeBase): """Response schema for Docker volume.""" id: int host_id: str last_update_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class DockerVolumeListResponse(BaseModel): """Response for listing volumes.""" volumes: List[DockerVolumeResponse] total: int # === Alert Schemas === class DockerAlertBase(BaseModel): """Base schema for Docker alert.""" container_name: str = Field(..., description="Container name") severity: Literal["warning", "error", "critical"] = Field("warning", description="Alert severity") message: Optional[str] = Field(None, description="Alert message") class DockerAlertResponse(DockerAlertBase): """Response schema for Docker alert.""" id: int host_id: str state: Literal["open", "closed", "acknowledged"] = "open" opened_at: datetime closed_at: Optional[datetime] = None acknowledged_at: Optional[datetime] = None acknowledged_by: Optional[str] = None last_notified_at: Optional[datetime] = None host_name: Optional[str] = None model_config = ConfigDict(from_attributes=True) class DockerAlertListResponse(BaseModel): """Response for listing alerts.""" alerts: List[DockerAlertResponse] total: int open_count: int acknowledged_count: int class DockerAlertAcknowledgeRequest(BaseModel): """Request to acknowledge an alert.""" note: Optional[str] = Field(None, description="Optional note for acknowledgement") # === Docker Host Schemas === class DockerHostInfo(BaseModel): """Docker information for a host.""" host_id: str host_name: str host_ip: str docker_enabled: bool = False docker_version: Optional[str] = None docker_status: Optional[Literal["online", "offline", "error"]] = None docker_last_collect_at: Optional[datetime] = None containers_total: int = 0 containers_running: int = 0 images_total: int = 0 volumes_total: int = 0 open_alerts: int = 0 model_config = ConfigDict(from_attributes=True) class DockerHostListResponse(BaseModel): """Response for listing Docker hosts.""" hosts: List[DockerHostInfo] total: int enabled: int class EnableDockerRequest(BaseModel): """Request to enable Docker monitoring on a host.""" enabled: bool = Field(True, description="Enable or disable Docker monitoring") # === Container Action Schemas === class ContainerActionResponse(BaseModel): """Response for container actions.""" success: bool message: str container_id: str action: str output: Optional[str] = None error: Optional[str] = None class ContainerLogsRequest(BaseModel): """Request for container logs.""" tail: int = Field(200, ge=1, le=5000, description="Number of lines to retrieve") timestamps: bool = Field(False, description="Include timestamps") since: Optional[str] = Field(None, description="Show logs since timestamp or relative time") class ContainerLogsResponse(BaseModel): """Response for container logs.""" container_id: str container_name: str logs: str lines: int class ContainerInspectResponse(BaseModel): """Response for container inspect.""" container_id: str container_name: str inspect_data: Dict[str, Any] # === Collection Schemas === class DockerCollectRequest(BaseModel): """Request to trigger Docker collection.""" force: bool = Field(False, description="Force collection even if recently collected") class DockerCollectResponse(BaseModel): """Response for Docker collection.""" success: bool host_id: str host_name: str message: str docker_version: Optional[str] = None containers_count: int = 0 images_count: int = 0 volumes_count: int = 0 duration_ms: int = 0 error: Optional[str] = None class DockerCollectAllResponse(BaseModel): """Response for collecting all Docker hosts.""" success: bool message: str total_hosts: int successful: int failed: int results: List[DockerCollectResponse] # === Stats Schemas === class DockerStatsResponse(BaseModel): """Global Docker statistics.""" total_hosts: int enabled_hosts: int online_hosts: int total_containers: int running_containers: int total_images: int total_volumes: int open_alerts: int last_collection: Optional[datetime] = None # === Image Action Schemas === class ImageActionResponse(BaseModel): """Response for image actions.""" success: bool message: str image_id: str action: str output: Optional[str] = None error: Optional[str] = None # === Extended Image Response with usage info === class DockerImageExtendedResponse(DockerImageBase): """Response schema for Docker image with usage info.""" id: int host_id: str created: Optional[datetime] = None last_update_at: Optional[datetime] = None in_use: bool = Field(True, description="Whether the image is used by at least one container") model_config = ConfigDict(from_attributes=True) class DockerImageExtendedListResponse(BaseModel): """Response for listing images with usage info.""" images: List[DockerImageExtendedResponse] total: int total_size: int unused_count: int = 0 # === Aggregated Containers Response === class DockerContainerAggregatedResponse(DockerContainerBase): """Response schema for Docker container with host info (for aggregated view).""" id: int host_id: str host_name: str host_ip: str customization: Optional[ContainerCustomizationOut] = None created_at: Optional[datetime] = None last_update_at: Optional[datetime] = None model_config = ConfigDict(from_attributes=True) class DockerContainerAggregatedListResponse(BaseModel): """Response for listing all containers across all hosts.""" containers: List[DockerContainerAggregatedResponse] total: int running: int stopped: int paused: int hosts_count: int last_update: Optional[datetime] = None