"""Модели запросов/ответов REST API веб-интерфейса. Автор: Сергей Антропов Сайт: https://devops.org.ru """ from __future__ import annotations from typing import Any, Literal from pydantic import BaseModel, Field class ClusterCreateRequest(BaseModel): """Тело POST /api/v1/clusters — создание кластера.""" name: str = Field(..., min_length=1, max_length=63, description="DNS-имя кластера (a-z0-9-)") kubernetes_version: str = Field( ..., min_length=1, description="Версия Kubernetes / тег kindest/node, например 1.29.4 или v1.29.4", ) workers: int = Field(2, ge=0, le=20, description="Число worker-нод (0–20)") class ClusterCreateAccepted(BaseModel): """Ответ 202 — задание поставлено в очередь.""" job_id: str status: Literal["queued"] = "queued" message: str = "Создание кластера выполняется в фоне; опросите GET /api/v1/jobs/{job_id}" class JobView(BaseModel): """Статус фонового задания.""" job_id: str kind: str status: Literal["queued", "running", "success", "failed", "cancelled"] cluster_name: str | None created_at_utc: str message: str | None = None result: dict[str, Any] | None = None progress_stage: str | None = Field(default=None, description="Текущий этап операции (пока задание активно)") progress_percent: int | None = Field(default=None, description="Прогресс 0–100 для индикатора в UI") progress_log: list[str] = Field( default_factory=list, description="Хвост журнала (скачивание образа, длительные команды, этапы); при опросе GET /jobs/{id}", ) class ClusterSummary(BaseModel): """Элемент списка кластеров.""" name: str registered_in_kind: bool has_local_kubeconfig: bool meta: dict[str, Any] = Field(default_factory=dict) class KindNodeResourceStat(BaseModel): """Одна нода kind (контейнер): снимок ``docker stats`` / ``podman stats``.""" container_name: str cpu_percent: str | None = Field(default=None, description="Доля CPU, напр. 1.23%") memory_usage: str | None = Field(default=None, description="Использование / лимит, напр. 120MiB / 7.7GiB") memory_percent: str | None = Field(default=None, description="Процент памяти от лимита контейнера") net_io: str | None = Field(default=None, description="Сетевой I/O (накопительно за жизнь контейнера)") block_io: str | None = Field(default=None, description="Блочный I/O") pids: int | None = Field(default=None, description="Число процессов в контейнере") class KindClusterResources(BaseModel): """Ресурсы узлов одного кластера kind (только запущенные контейнеры).""" cluster_name: str nodes: list[KindNodeResourceStat] = Field(default_factory=list) note: str | None = Field(default=None, description="Пояснение, если узлов нет или ошибка списка") class StatsResponse(BaseModel): """Краткая статистика для дашборда.""" kind_clusters_count: int local_cluster_dirs_count: int total_workers_from_meta: int | None jobs_total: int jobs_recent_failed: int cluster_resources: list[KindClusterResources] = Field( default_factory=list, description="CPU/RAM/I/O узлов kind по данным container CLI", ) cluster_resources_error: str | None = Field( default=None, description="Глобальная ошибка сбора метрик (например CLI не найден)", ) class ClusterWorkloadsResponse(BaseModel): """Вывод kubectl по кластеру (узлы и поды).""" cluster_name: str nodes_rc: int | None = None nodes_output: str | None = None pods_rc: int | None = None pods_output: str | None = None error: str | None = None