""" Schémas Pydantic pour l'exécution Ansible. """ from datetime import datetime, timezone from typing import Optional, List, Dict, Any, Literal from pydantic import BaseModel, Field, field_validator class AnsibleExecutionRequest(BaseModel): """Requête d'exécution de playbook Ansible.""" playbook: str = Field(..., description="Nom du playbook à exécuter") target: str = Field(default="all", description="Hôte ou groupe cible") extra_vars: Optional[Dict[str, Any]] = Field(default=None, description="Variables supplémentaires") check_mode: bool = Field(default=False, description="Mode dry-run (--check)") verbose: bool = Field(default=False, description="Mode verbeux") class AdHocCommandRequest(BaseModel): """Requête pour exécuter une commande ad-hoc Ansible.""" target: str = Field(..., description="Hôte ou groupe cible") command: str = Field(..., description="Commande shell à exécuter") module: str = Field(default="shell", description="Module Ansible (shell, command, raw)") become: bool = Field(default=False, description="Exécuter avec sudo") timeout: int = Field(default=60, ge=5, le=600, description="Timeout en secondes") category: Optional[str] = Field(default="default", description="Catégorie d'historique pour cette commande") class AdHocCommandResult(BaseModel): """Résultat d'une commande ad-hoc.""" target: str command: str success: bool return_code: int stdout: str stderr: Optional[str] = None duration: float hosts_results: Optional[Dict[str, Any]] = None class AdHocHistoryEntry(BaseModel): """Entrée dans l'historique des commandes ad-hoc.""" id: str command: str target: str module: str become: bool category: str = "default" description: Optional[str] = None created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) last_used: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) use_count: int = 1 class AdHocHistoryCategory(BaseModel): """Catégorie pour organiser les commandes ad-hoc.""" name: str description: Optional[str] = None color: str = "#7c3aed" icon: str = "fa-folder" class PlaybookInfo(BaseModel): """Informations sur un playbook.""" name: str filename: str path: str category: str = "general" subcategory: str = "other" hosts: str = "all" size: int = 0 modified: Optional[str] = None description: Optional[str] = None class PlaybookContentRequest(BaseModel): """Requête pour sauvegarder le contenu d'un playbook.""" content: str = Field(..., description="Contenu YAML du playbook") class PlaybooksListResponse(BaseModel): """Réponse API pour la liste des playbooks.""" playbooks: List[PlaybookInfo] = Field(default_factory=list) categories: Dict[str, List[str]] = Field(default_factory=dict) ansible_dir: str = "" filter: Optional[str] = None class BootstrapRequest(BaseModel): """Requête de bootstrap pour un hôte.""" host: str = Field(..., description="Adresse IP ou hostname de l'hôte") root_password: str = Field(..., description="Mot de passe root pour la connexion initiale") automation_user: str = Field(default="automation", description="Nom de l'utilisateur d'automatisation à créer") class SSHConfigResponse(BaseModel): """Réponse de diagnostic de la configuration SSH.""" ssh_key_path: str ssh_dir: str ssh_dir_exists: bool private_key_exists: bool public_key_exists: bool available_files: List[str] = Field(default_factory=list) public_keys_found: List[str] = Field(default_factory=list) active_private_key: Optional[str] = None ssh_user: str sshpass_available: bool