165 lines
6.6 KiB
Python
165 lines
6.6 KiB
Python
"""
|
|
Endpoints API pour l'intégration OPNsense (Kea DHCP)
|
|
"""
|
|
import traceback
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.orm import Session
|
|
from pydantic import BaseModel
|
|
from typing import Optional
|
|
|
|
from backend.app.core.database import get_db
|
|
from backend.app.core.config import config_manager
|
|
from backend.app.models.ip import IP
|
|
from backend.app.services.opnsense_client import OPNsenseClient, OPNsenseAPIError
|
|
|
|
router = APIRouter(prefix="/api/opnsense", tags=["OPNsense"])
|
|
|
|
|
|
class DHCPReservationRequest(BaseModel):
|
|
"""Schéma pour créer/mettre à jour une réservation DHCP"""
|
|
ip_address: str
|
|
hw_address: str
|
|
hostname: str = ""
|
|
description: str = "Ajouté par IPWatch"
|
|
|
|
|
|
def get_opnsense_client() -> OPNsenseClient:
|
|
"""Retourne un client OPNsense configuré"""
|
|
config = config_manager.config.opnsense
|
|
print(f"[OPNsense Router] Config: enabled={config.enabled}, host={config.host}, api_key={'***' + config.api_key[-8:] if config.api_key else 'VIDE'}")
|
|
if not config.enabled:
|
|
raise HTTPException(status_code=503, detail="Intégration OPNsense désactivée")
|
|
if not config.host or not config.api_key:
|
|
raise HTTPException(status_code=503, detail="Configuration OPNsense incomplète")
|
|
return OPNsenseClient()
|
|
|
|
|
|
@router.get("/status")
|
|
async def opnsense_status():
|
|
"""Teste la connexion à l'API OPNsense"""
|
|
client = get_opnsense_client()
|
|
try:
|
|
result = await client.test_connection()
|
|
return {"status": "connected", "data": result}
|
|
except Exception as e:
|
|
print(f"[OPNsense Router] Erreur status: {type(e).__name__}: {e}")
|
|
traceback.print_exc()
|
|
raise HTTPException(status_code=502, detail=f"Connexion OPNsense échouée: {type(e).__name__}: {str(e)}")
|
|
|
|
|
|
@router.get("/dhcp/reservations")
|
|
async def list_reservations():
|
|
"""Liste toutes les réservations DHCP Kea"""
|
|
client = get_opnsense_client()
|
|
try:
|
|
result = await client.search_reservations()
|
|
return result
|
|
except Exception as e:
|
|
print(f"[OPNsense Router] Erreur list_reservations: {type(e).__name__}: {e}")
|
|
traceback.print_exc()
|
|
raise HTTPException(status_code=502, detail=f"Erreur récupération réservations: {type(e).__name__}: {str(e)}")
|
|
|
|
|
|
@router.get("/dhcp/reservation/{ip_address}")
|
|
async def get_reservation_by_ip(ip_address: str):
|
|
"""Cherche une réservation DHCP par adresse IP"""
|
|
client = get_opnsense_client()
|
|
try:
|
|
reservation = await client.find_reservation_by_ip(ip_address)
|
|
if reservation:
|
|
return {"found": True, "reservation": reservation}
|
|
return {"found": False, "reservation": None}
|
|
except Exception as e:
|
|
print(f"[OPNsense Router] Erreur get_reservation_by_ip: {type(e).__name__}: {e}")
|
|
traceback.print_exc()
|
|
raise HTTPException(status_code=502, detail=f"Erreur recherche réservation: {type(e).__name__}: {str(e)}")
|
|
|
|
|
|
@router.post("/dhcp/reservation")
|
|
async def upsert_reservation(
|
|
request: DHCPReservationRequest,
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
Crée ou met à jour une réservation DHCP Kea pour une IP.
|
|
Après succès, met à jour dhcp_synced=True dans la BDD.
|
|
"""
|
|
print(f"[OPNsense Router] === UPSERT RESERVATION ===")
|
|
print(f"[OPNsense Router] IP: {request.ip_address}, MAC: {request.hw_address}, Hostname: {request.hostname}")
|
|
|
|
client = get_opnsense_client()
|
|
|
|
try:
|
|
# Étape 0 : Résoudre le subnet UUID
|
|
print(f"[OPNsense Router] Étape 0: Résolution du subnet pour {request.ip_address}...")
|
|
subnet_uuid = await client.find_subnet_for_ip(request.ip_address)
|
|
if not subnet_uuid:
|
|
raise HTTPException(status_code=400, detail=f"Aucun subnet Kea trouvé pour l'IP {request.ip_address}")
|
|
|
|
reservation_data = {
|
|
"subnet": subnet_uuid,
|
|
"ip_address": request.ip_address,
|
|
"hw_address": request.hw_address,
|
|
"hostname": request.hostname,
|
|
"description": request.description
|
|
}
|
|
print(f"[OPNsense Router] Données réservation: {reservation_data}")
|
|
|
|
# Étape 1 : Chercher si une réservation existe déjà
|
|
print(f"[OPNsense Router] Étape 1: Recherche réservation existante...")
|
|
existing = await client.find_reservation_by_ip(request.ip_address)
|
|
|
|
if existing:
|
|
# Mise à jour de la réservation existante
|
|
uuid = existing.get("uuid")
|
|
print(f"[OPNsense Router] Étape 2: Mise à jour réservation existante uuid={uuid}")
|
|
if not uuid:
|
|
raise HTTPException(status_code=500, detail="UUID de réservation introuvable")
|
|
result = await client.set_reservation(uuid, reservation_data)
|
|
action = "updated"
|
|
else:
|
|
# Création d'une nouvelle réservation
|
|
print(f"[OPNsense Router] Étape 2: Création nouvelle réservation")
|
|
result = await client.add_reservation(reservation_data)
|
|
action = "created"
|
|
|
|
print(f"[OPNsense Router] Étape 2 terminée: action={action}, result={result}")
|
|
|
|
# Étape 3 : Appliquer les changements dans Kea
|
|
print(f"[OPNsense Router] Étape 3: Reconfiguration Kea...")
|
|
await client.reconfigure_kea()
|
|
print(f"[OPNsense Router] Étape 3 terminée: Kea reconfiguré")
|
|
|
|
# Étape 4 : Mettre à jour dhcp_synced dans la BDD
|
|
print(f"[OPNsense Router] Étape 4: Mise à jour BDD dhcp_synced=True")
|
|
ip_record = db.query(IP).filter(IP.ip == request.ip_address).first()
|
|
if ip_record:
|
|
ip_record.dhcp_synced = True
|
|
db.commit()
|
|
db.refresh(ip_record)
|
|
print(f"[OPNsense Router] Étape 4 terminée: BDD mise à jour")
|
|
else:
|
|
print(f"[OPNsense Router] ATTENTION: IP {request.ip_address} non trouvée en BDD")
|
|
|
|
print(f"[OPNsense Router] === SUCCÈS: {action} ===")
|
|
return {
|
|
"status": "success",
|
|
"action": action,
|
|
"ip_address": request.ip_address,
|
|
"result": result
|
|
}
|
|
|
|
except HTTPException:
|
|
raise
|
|
except OPNsenseAPIError as e:
|
|
print(f"[OPNsense Router] === ERREUR VALIDATION ===")
|
|
print(f"[OPNsense Router] Message: {str(e)}")
|
|
print(f"[OPNsense Router] Validations: {e.validations}")
|
|
raise HTTPException(status_code=422, detail=str(e))
|
|
except Exception as e:
|
|
print(f"[OPNsense Router] === ERREUR ===")
|
|
print(f"[OPNsense Router] Type: {type(e).__name__}")
|
|
print(f"[OPNsense Router] Message: {str(e)}")
|
|
traceback.print_exc()
|
|
raise HTTPException(status_code=502, detail=f"Erreur OPNsense: {type(e).__name__}: {str(e)}")
|