183 lines
5.4 KiB
Python
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))
|