259 lines
7.1 KiB
Python
259 lines
7.1 KiB
Python
"""
|
|
Exceptions personnalisées de l'application.
|
|
|
|
Centralise la gestion des erreurs avec des exceptions typées.
|
|
"""
|
|
|
|
from typing import Any, Dict, Optional
|
|
|
|
|
|
class HomelabException(Exception):
|
|
"""Exception de base pour l'application Homelab."""
|
|
|
|
def __init__(
|
|
self,
|
|
message: str,
|
|
status_code: int = 500,
|
|
details: Optional[Dict[str, Any]] = None
|
|
):
|
|
self.message = message
|
|
self.status_code = status_code
|
|
self.details = details or {}
|
|
super().__init__(self.message)
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""Convertit l'exception en dictionnaire pour la réponse API."""
|
|
return {
|
|
"error": self.__class__.__name__,
|
|
"message": self.message,
|
|
"details": self.details,
|
|
}
|
|
|
|
|
|
# === Exceptions 404 Not Found ===
|
|
|
|
class NotFoundException(HomelabException):
|
|
"""Exception de base pour les ressources non trouvées."""
|
|
|
|
def __init__(self, resource_type: str, identifier: str):
|
|
super().__init__(
|
|
message=f"{resource_type} '{identifier}' non trouvé(e)",
|
|
status_code=404,
|
|
details={"resource_type": resource_type, "identifier": identifier}
|
|
)
|
|
|
|
|
|
class HostNotFoundException(NotFoundException):
|
|
"""Hôte non trouvé."""
|
|
|
|
def __init__(self, identifier: str):
|
|
super().__init__("Hôte", identifier)
|
|
|
|
|
|
class TaskNotFoundException(NotFoundException):
|
|
"""Tâche non trouvée."""
|
|
|
|
def __init__(self, identifier: str):
|
|
super().__init__("Tâche", identifier)
|
|
|
|
|
|
class ScheduleNotFoundException(NotFoundException):
|
|
"""Schedule non trouvé."""
|
|
|
|
def __init__(self, identifier: str):
|
|
super().__init__("Schedule", identifier)
|
|
|
|
|
|
class PlaybookNotFoundException(NotFoundException):
|
|
"""Playbook non trouvé."""
|
|
|
|
def __init__(self, identifier: str):
|
|
super().__init__("Playbook", identifier)
|
|
|
|
|
|
class GroupNotFoundException(NotFoundException):
|
|
"""Groupe non trouvé."""
|
|
|
|
def __init__(self, identifier: str):
|
|
super().__init__("Groupe", identifier)
|
|
|
|
|
|
class LogNotFoundException(NotFoundException):
|
|
"""Log non trouvé."""
|
|
|
|
def __init__(self, identifier: str):
|
|
super().__init__("Log", identifier)
|
|
|
|
|
|
# === Exceptions 400 Bad Request ===
|
|
|
|
class ValidationException(HomelabException):
|
|
"""Erreur de validation des données."""
|
|
|
|
def __init__(self, message: str, field: Optional[str] = None):
|
|
details = {"field": field} if field else {}
|
|
super().__init__(
|
|
message=message,
|
|
status_code=400,
|
|
details=details
|
|
)
|
|
|
|
|
|
class DuplicateResourceException(HomelabException):
|
|
"""Ressource déjà existante."""
|
|
|
|
def __init__(self, resource_type: str, identifier: str):
|
|
super().__init__(
|
|
message=f"{resource_type} '{identifier}' existe déjà",
|
|
status_code=400,
|
|
details={"resource_type": resource_type, "identifier": identifier}
|
|
)
|
|
|
|
|
|
class InvalidOperationException(HomelabException):
|
|
"""Opération non valide dans l'état actuel."""
|
|
|
|
def __init__(self, message: str, current_state: Optional[str] = None):
|
|
details = {"current_state": current_state} if current_state else {}
|
|
super().__init__(
|
|
message=message,
|
|
status_code=400,
|
|
details=details
|
|
)
|
|
|
|
|
|
class IncompatiblePlaybookException(HomelabException):
|
|
"""Playbook incompatible avec la cible."""
|
|
|
|
def __init__(self, playbook: str, target: str, playbook_hosts: str):
|
|
super().__init__(
|
|
message=f"Le playbook '{playbook}' (hosts: {playbook_hosts}) n'est pas compatible avec la cible '{target}'",
|
|
status_code=400,
|
|
details={
|
|
"playbook": playbook,
|
|
"target": target,
|
|
"playbook_hosts": playbook_hosts,
|
|
}
|
|
)
|
|
|
|
|
|
# === Exceptions 401/403 Auth ===
|
|
|
|
class AuthenticationException(HomelabException):
|
|
"""Erreur d'authentification."""
|
|
|
|
def __init__(self, message: str = "Authentification requise"):
|
|
super().__init__(message=message, status_code=401)
|
|
|
|
|
|
class AuthorizationException(HomelabException):
|
|
"""Erreur d'autorisation."""
|
|
|
|
def __init__(self, message: str = "Accès non autorisé"):
|
|
super().__init__(message=message, status_code=403)
|
|
|
|
|
|
# === Exceptions 500 Server Error ===
|
|
|
|
class AnsibleExecutionException(HomelabException):
|
|
"""Erreur lors de l'exécution Ansible."""
|
|
|
|
def __init__(
|
|
self,
|
|
message: str,
|
|
return_code: int = -1,
|
|
stdout: str = "",
|
|
stderr: str = ""
|
|
):
|
|
super().__init__(
|
|
message=message,
|
|
status_code=500,
|
|
details={
|
|
"return_code": return_code,
|
|
"stdout": stdout,
|
|
"stderr": stderr,
|
|
}
|
|
)
|
|
|
|
|
|
class BootstrapException(HomelabException):
|
|
"""Erreur lors du bootstrap d'un hôte."""
|
|
|
|
def __init__(
|
|
self,
|
|
host: str,
|
|
message: str,
|
|
return_code: int = -1,
|
|
stdout: str = "",
|
|
stderr: str = ""
|
|
):
|
|
super().__init__(
|
|
message=f"Échec bootstrap pour {host}: {message}",
|
|
status_code=500,
|
|
details={
|
|
"host": host,
|
|
"return_code": return_code,
|
|
"stdout": stdout,
|
|
"stderr": stderr,
|
|
}
|
|
)
|
|
|
|
|
|
class SSHConnectionException(HomelabException):
|
|
"""Erreur de connexion SSH."""
|
|
|
|
def __init__(self, host: str, message: str):
|
|
super().__init__(
|
|
message=f"Connexion SSH échouée vers {host}: {message}",
|
|
status_code=500,
|
|
details={"host": host}
|
|
)
|
|
|
|
|
|
class DatabaseException(HomelabException):
|
|
"""Erreur de base de données."""
|
|
|
|
def __init__(self, message: str, operation: Optional[str] = None):
|
|
details = {"operation": operation} if operation else {}
|
|
super().__init__(
|
|
message=f"Erreur base de données: {message}",
|
|
status_code=500,
|
|
details=details
|
|
)
|
|
|
|
|
|
class SchedulerException(HomelabException):
|
|
"""Erreur du service de planification."""
|
|
|
|
def __init__(self, message: str, schedule_id: Optional[str] = None):
|
|
details = {"schedule_id": schedule_id} if schedule_id else {}
|
|
super().__init__(
|
|
message=f"Erreur scheduler: {message}",
|
|
status_code=500,
|
|
details=details
|
|
)
|
|
|
|
|
|
class NotificationException(HomelabException):
|
|
"""Erreur lors de l'envoi de notification."""
|
|
|
|
def __init__(self, message: str, topic: Optional[str] = None):
|
|
details = {"topic": topic} if topic else {}
|
|
super().__init__(
|
|
message=f"Erreur notification: {message}",
|
|
status_code=500,
|
|
details=details
|
|
)
|
|
|
|
|
|
class FileOperationException(HomelabException):
|
|
"""Erreur lors d'une opération sur fichier."""
|
|
|
|
def __init__(self, message: str, path: Optional[str] = None):
|
|
details = {"path": path} if path else {}
|
|
super().__init__(
|
|
message=message,
|
|
status_code=500,
|
|
details=details
|
|
)
|