""" Service de gestion du statut de bootstrap des hôtes. """ import asyncio from datetime import datetime, timezone from typing import Dict, Optional from sqlalchemy.ext.asyncio import AsyncSession from app.models.database import async_session_maker class BootstrapStatusService: """Service pour gérer le statut de bootstrap des hôtes. Cette version utilise la base de données SQLite via SQLAlchemy async. Note: Le modèle BD utilise host_id (FK), mais ce service utilise host_name pour la compatibilité avec le code existant. Il fait la correspondance via HostRepository. """ def __init__(self): # Cache en mémoire pour éviter les requêtes BD répétées self._cache: Dict[str, Dict] = {} async def _get_host_id_by_name(self, session: AsyncSession, host_name: str) -> Optional[str]: """Récupère l'ID d'un hôte par son nom.""" from app.crud.host import HostRepository repo = HostRepository(session) host = await repo.get_by_name(host_name) return host.id if host else None def set_bootstrap_status(self, host_name: str, success: bool, details: str = None) -> Dict: """Enregistre le statut de bootstrap d'un hôte (version synchrone avec cache).""" status_data = { "bootstrap_ok": success, "bootstrap_date": datetime.now(timezone.utc).isoformat(), "details": details } self._cache[host_name] = status_data # Planifier la sauvegarde en BD de manière asynchrone asyncio.create_task(self._save_to_db(host_name, success, details)) return status_data async def _save_to_db(self, host_name: str, success: bool, details: str = None): """Sauvegarde le statut dans la BD.""" try: async with async_session_maker() as session: host_id = await self._get_host_id_by_name(session, host_name) if not host_id: print(f"Host '{host_name}' non trouvé en BD pour bootstrap status") return from app.crud.bootstrap_status import BootstrapStatusRepository repo = BootstrapStatusRepository(session) await repo.create( host_id=host_id, status="success" if success else "failed", last_attempt=datetime.now(timezone.utc), error_message=None if success else details, ) await session.commit() except Exception as e: print(f"Erreur sauvegarde bootstrap status en BD: {e}") def get_bootstrap_status(self, host_name: str) -> Dict: """Récupère le statut de bootstrap d'un hôte depuis le cache.""" return self._cache.get(host_name, { "bootstrap_ok": False, "bootstrap_date": None, "details": None }) def get_all_status(self) -> Dict[str, Dict]: """Récupère le statut de tous les hôtes depuis le cache.""" return self._cache.copy() def remove_host(self, host_name: str) -> bool: """Supprime le statut d'un hôte du cache.""" if host_name in self._cache: del self._cache[host_name] return True return False async def load_from_db(self): """Charge tous les statuts depuis la BD dans le cache (appelé au démarrage).""" try: async with async_session_maker() as session: from sqlalchemy import select from app.models.bootstrap_status import BootstrapStatus from app.models.host import Host # Récupérer tous les derniers statuts avec les noms d'hôtes stmt = ( select(BootstrapStatus, Host.name) .join(Host, BootstrapStatus.host_id == Host.id) .order_by(BootstrapStatus.created_at.desc()) ) result = await session.execute(stmt) # Garder seulement le dernier statut par hôte seen_hosts = set() for bs, host_name in result: if host_name not in seen_hosts: self._cache[host_name] = { "bootstrap_ok": bs.status == "success", "bootstrap_date": bs.last_attempt.isoformat() if bs.last_attempt else bs.created_at.isoformat(), "details": bs.error_message } seen_hosts.add(host_name) print(f"📋 {len(self._cache)} statut(s) bootstrap chargé(s) depuis la BD") except Exception as e: print(f"Erreur chargement bootstrap status depuis BD: {e}") # Instance singleton du service bootstrap_status_service = BootstrapStatusService()