homelab_automation/app/routes/builtin_playbooks.py

175 lines
5.7 KiB
Python

"""
Routes API pour les builtin playbooks (collecte métriques).
"""
import asyncio
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.config import settings
from app.core.dependencies import get_db, verify_api_key
from app.crud.host import HostRepository
from app.crud.host_metrics import HostMetricsRepository
from app.services import ansible_service, ws_manager
from app.services.builtin_playbooks import (
BuiltinPlaybookService,
BUILTIN_PLAYBOOKS,
get_builtin_playbook_service,
init_builtin_playbook_service,
)
router = APIRouter()
def _get_service() -> BuiltinPlaybookService:
"""Récupère ou initialise le service builtin playbooks."""
try:
return get_builtin_playbook_service()
except RuntimeError:
return init_builtin_playbook_service(settings.ansible_dir, ansible_service)
@router.get("")
async def list_builtin_playbooks(api_key_valid: bool = Depends(verify_api_key)):
"""Liste tous les builtin playbooks disponibles."""
return [pb.model_dump() for pb in BUILTIN_PLAYBOOKS.values()]
@router.get("/{builtin_id}")
async def get_builtin_playbook(
builtin_id: str,
api_key_valid: bool = Depends(verify_api_key)
):
"""Récupère les détails d'un builtin playbook."""
if builtin_id not in BUILTIN_PLAYBOOKS:
raise HTTPException(status_code=404, detail=f"Builtin playbook '{builtin_id}' non trouvé")
return BUILTIN_PLAYBOOKS[builtin_id].model_dump()
@router.post("/execute")
async def execute_builtin_playbook(
builtin_id: str,
target: str,
api_key_valid: bool = Depends(verify_api_key),
db_session: AsyncSession = Depends(get_db)
):
"""Exécute un builtin playbook sur une cible."""
if builtin_id not in BUILTIN_PLAYBOOKS:
raise HTTPException(status_code=404, detail=f"Builtin playbook '{builtin_id}' non trouvé")
service = _get_service()
result = await service.execute_builtin(builtin_id, target)
# Si collecte de métriques, sauvegarder en BD
if BUILTIN_PLAYBOOKS[builtin_id].collect_metrics and result.get("success"):
metrics_repo = HostMetricsRepository(db_session)
host_repo = HostRepository(db_session)
for hostname, metrics_data in result.get("parsed_metrics", {}).items():
# Trouver l'host_id
host = await host_repo.get_by_name(hostname)
if host:
metrics_create = service.create_metrics_from_parsed(
host_id=host.id,
parsed_data=metrics_data,
builtin_id=builtin_id,
execution_time_ms=result.get("execution_time_ms", 0)
)
await metrics_repo.create(**metrics_create.model_dump())
await db_session.commit()
await ws_manager.broadcast({
"type": "builtin_executed",
"data": {
"builtin_id": builtin_id,
"target": target,
"success": result.get("success", False)
}
})
return result
@router.post("/execute-background")
async def execute_builtin_playbook_background(
builtin_id: str,
target: str,
background_tasks: BackgroundTasks,
api_key_valid: bool = Depends(verify_api_key)
):
"""Exécute un builtin playbook en arrière-plan."""
if builtin_id not in BUILTIN_PLAYBOOKS:
raise HTTPException(status_code=404, detail=f"Builtin playbook '{builtin_id}' non trouvé")
async def run_in_background():
service = _get_service()
result = await service.execute_builtin(builtin_id, target)
await ws_manager.broadcast({
"type": "builtin_executed",
"data": {
"builtin_id": builtin_id,
"target": target,
"success": result.get("success", False)
}
})
# Planifier l'exécution en arrière-plan
background_tasks.add_task(asyncio.create_task, run_in_background())
return {
"message": f"Builtin playbook '{builtin_id}' planifié pour exécution sur {target}",
"builtin_id": builtin_id,
"target": target
}
@router.post("/collect-all")
async def collect_all_metrics(
background_tasks: BackgroundTasks,
api_key_valid: bool = Depends(verify_api_key),
db_session: AsyncSession = Depends(get_db)
):
"""Collecte les métriques de tous les hôtes."""
host_repo = HostRepository(db_session)
hosts = await host_repo.list(limit=1000)
if not hosts:
return {"message": "Aucun hôte trouvé", "hosts_count": 0}
async def collect_for_all():
service = _get_service()
results = []
for host in hosts:
try:
result = await service.execute_builtin("collect_system_info", host.name)
results.append({
"host": host.name,
"success": result.get("success", False)
})
except Exception as e:
results.append({
"host": host.name,
"success": False,
"error": str(e)
})
await ws_manager.broadcast({
"type": "metrics_collection_complete",
"data": {
"total": len(hosts),
"success": sum(1 for r in results if r.get("success")),
"failed": sum(1 for r in results if not r.get("success"))
}
})
background_tasks.add_task(asyncio.create_task, collect_for_all())
return {
"message": f"Collecte des métriques lancée pour {len(hosts)} hôte(s)",
"hosts_count": len(hosts)
}