""" Routes API pour le bootstrap des hôtes. """ import asyncio from datetime import datetime, timezone from typing import Optional from fastapi import APIRouter, Depends, HTTPException from app.core.dependencies import verify_api_key from app.schemas.ansible import BootstrapRequest from app.schemas.common import CommandResult, LogEntry from app.services import ( bootstrap_status_service, ws_manager, notification_service, db, ) from app.utils.ssh_utils import bootstrap_host router = APIRouter() @router.get("/status") async def get_all_bootstrap_status(api_key_valid: bool = Depends(verify_api_key)): """Récupère le statut de bootstrap de tous les hôtes.""" return { "hosts": bootstrap_status_service.get_all_status() } @router.get("/status/{host_name}") async def get_host_bootstrap_status( host_name: str, api_key_valid: bool = Depends(verify_api_key) ): """Récupère le statut de bootstrap d'un hôte spécifique.""" status = bootstrap_status_service.get_bootstrap_status(host_name) return { "host": host_name, **status } @router.post("/status/{host_name}") async def set_host_bootstrap_status( host_name: str, success: bool = True, details: Optional[str] = None, api_key_valid: bool = Depends(verify_api_key) ): """Définit manuellement le statut de bootstrap d'un hôte.""" result = bootstrap_status_service.set_bootstrap_status( host_name=host_name, success=success, details=details or f"Status défini manuellement" ) db.invalidate_hosts_cache() await ws_manager.broadcast({ "type": "bootstrap_status_updated", "data": { "host": host_name, "bootstrap_ok": success } }) return { "host": host_name, "status": "updated", **result } @router.post("", response_model=CommandResult) async def bootstrap_ansible_host( request: BootstrapRequest, api_key_valid: bool = Depends(verify_api_key) ): """Bootstrap un hôte pour Ansible. Cette opération: 1. Se connecte à l'hôte via SSH avec le mot de passe root 2. Crée l'utilisateur d'automatisation 3. Configure la clé SSH publique 4. Configure sudo sans mot de passe 5. Installe Python3 6. Vérifie la connexion SSH par clé """ import logging import traceback logger = logging.getLogger("bootstrap_endpoint") try: logger.info(f"Bootstrap request for host={request.host}, user={request.automation_user}") result = bootstrap_host( host=request.host, root_password=request.root_password, automation_user=request.automation_user ) logger.info(f"Bootstrap result: status={result.status}, return_code={result.return_code}") if result.return_code != 0: raise HTTPException( status_code=500, detail={ "status": result.status, "return_code": result.return_code, "stdout": result.stdout, "stderr": result.stderr } ) # Trouver le nom de l'hôte host_name = request.host for h in db.hosts: if h.ip == request.host or h.name == request.host: host_name = h.name break # Enregistrer le statut de bootstrap réussi bootstrap_status_service.set_bootstrap_status( host_name=host_name, success=True, details=f"Bootstrap réussi via API (user: {request.automation_user})" ) db.invalidate_hosts_cache() log_entry = LogEntry( id=db.get_next_id("logs"), timestamp=datetime.now(timezone.utc), level="INFO", message=f"Bootstrap réussi pour {host_name} (user: {request.automation_user})", source="bootstrap", host=host_name ) db.logs.insert(0, log_entry) await ws_manager.broadcast({ "type": "bootstrap_success", "data": { "host": host_name, "user": request.automation_user, "status": "ok", "bootstrap_ok": True } }) asyncio.create_task(notification_service.notify_bootstrap_success(host_name)) return result except HTTPException as http_exc: error_detail = str(http_exc.detail) if http_exc.detail else "Erreur inconnue" asyncio.create_task(notification_service.notify_bootstrap_failed( hostname=request.host, error=error_detail[:200] )) raise except Exception as e: logger.error(f"Bootstrap exception: {e}") logger.error(traceback.format_exc()) log_entry = LogEntry( id=db.get_next_id("logs"), timestamp=datetime.now(timezone.utc), level="ERROR", message=f"Échec bootstrap pour {request.host}: {str(e)}", source="bootstrap", host=request.host ) db.logs.insert(0, log_entry) asyncio.create_task(notification_service.notify_bootstrap_failed( hostname=request.host, error=str(e)[:200] )) raise HTTPException(status_code=500, detail=str(e))