183 lines
5.4 KiB
Python

"""
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))