Code source de iziproxy.env_detector

"""
Module de détection automatique de l'environnement d'exécution
"""

import os
import re
import socket
import logging
import platform

# Configuration du logger
logger = logging.getLogger("iziproxy")


[docs] class EnvironmentDetector: """ Détecte automatiquement l'environnement d'exécution (local, dev, prod) Cette classe permet de: - Identifier l'environnement d'exécution actuel - Utiliser différentes méthodes de détection - Personnaliser les règles de détection """ ENV_TYPES = ["local", "dev", "prod"] ENV_VAR_NAMES = ["ENVIRONMENT", "ENV", "APP_ENV", "ENVIRONMENT_TYPE", "PROXY_ENV", "IZIPROXY_ENV"]
[docs] def __init__(self, config=None): """ Initialise le détecteur d'environnement Args: config (dict, optional): Configuration avec des règles de détection personnalisées """ self.config = config or {} self.detection_cache = None self.system_info = self._get_system_info()
[docs] def detect_environment(self, force_refresh=False): """ Détecte l'environnement actuel en utilisant différentes méthodes Args: force_refresh (bool): Force le rafraîchissement du cache Returns: str: Type d'environnement détecté ('local', 'dev', 'prod') """ # Utiliser le cache si disponible et pas de rafraîchissement forcé if self.detection_cache and not force_refresh: return self.detection_cache # Déterminer la méthode de détection method = self._get_detection_method() detected_env = None # Appliquer les méthodes de détection dans l'ordre if method == "env_var" or method == "auto": detected_env = self._detect_by_env_var() if not detected_env and (method == "hostname" or method == "auto"): detected_env = self._detect_by_hostname() if not detected_env and (method == "ip" or method == "auto"): detected_env = self._detect_by_ip() if not detected_env and method == "ask": detected_env = self._ask_user() # Par défaut, considérer comme environnement local if not detected_env: logger.info("Environnement non détecté, utilisation de 'local' par défaut") detected_env = "local" # Mettre en cache le résultat self.detection_cache = detected_env logger.info(f"Environnement détecté: {detected_env}") return detected_env
def _get_system_info(self): """ Collecte les informations système pour faciliter la détection Returns: dict: Informations système """ info = { "hostname": socket.gethostname().lower(), "os": platform.system().lower(), "ip": None, } # Tenter d'obtenir l'adresse IP locale try: info["ip"] = socket.gethostbyname(socket.gethostname()) except: pass return info def _get_detection_method(self): """ Détermine la méthode de détection à utiliser Returns: str: Méthode de détection ("auto", "env_var", "hostname", "ip", "ask") """ if "environment_detection" in self.config: detection_config = self.config["environment_detection"] if "method" in detection_config: return detection_config["method"].lower() return "auto" def _detect_by_env_var(self): """ Détecte l'environnement via les variables d'environnement Returns: str: Type d'environnement détecté ou None """ # Vérifier d'abord la variable spécifique à IziProxy if "IZIPROXY_ENV" in os.environ: env_value = os.environ["IZIPROXY_ENV"].lower() if env_value in self.ENV_TYPES: logger.debug(f"Environnement détecté via IZIPROXY_ENV: {env_value}") return env_value # Vérifier les autres variables d'environnement for var_name in self.ENV_VAR_NAMES: if var_name in os.environ: env_value = os.environ[var_name].lower() # Correspondance directe avec un type d'environnement if env_value in self.ENV_TYPES: logger.debug(f"Environnement détecté via variable {var_name}: {env_value}") return env_value # Recherche partielle (ex: "production" -> "prod") for env_type in self.ENV_TYPES: if env_type in env_value or env_value in env_type: logger.debug(f"Correspondance partielle via {var_name}: {env_value} -> {env_type}") return env_type return None def _detect_by_hostname(self): """ Détecte l'environnement via le nom d'hôte de la machine Returns: str: Type d'environnement détecté ou None """ hostname = self.system_info["hostname"] logger.debug(f"Détection par hostname: {hostname}") # Vérifier les motifs de nom d'hôte dans la configuration patterns = self._get_hostname_patterns() for env_type, host_patterns in patterns.items(): for pattern in host_patterns: if pattern.lower() in hostname: logger.debug(f"Environnement détecté via hostname pattern: {pattern} -> {env_type}") return env_type # Vérifier les regex de nom d'hôte regex_patterns = self._get_hostname_regex() for env_type, regex_list in regex_patterns.items(): for regex in regex_list: try: if re.search(regex, hostname, re.IGNORECASE): logger.debug(f"Environnement détecté via hostname regex: {regex} -> {env_type}") return env_type except re.error: logger.warning(f"Expression régulière invalide: {regex}") # Recherche basique par mots clés connus if any(word in hostname for word in ["prod", "production"]): return "prod" elif any(word in hostname for word in ["dev", "development", "staging", "test"]): return "dev" elif any(word in hostname for word in ["local", "laptop", "desktop", "pc-"]): return "local" return None def _detect_by_ip(self): """ Détecte l'environnement via l'adresse IP Returns: str: Type d'environnement détecté ou None """ # Récupérer les plages d'IP configurées ip_ranges = self._get_ip_ranges() if not ip_ranges: return None # Obtenir l'IP locale ip = self.system_info["ip"] if not ip: return None # Vérifier dans quelle plage se trouve l'IP for env_type, ip_range_list in ip_ranges.items(): for ip_range in ip_range_list: try: if self._ip_in_range(ip, ip_range): logger.debug(f"Environnement détecté via IP range: {ip} in {ip_range} -> {env_type}") return env_type except Exception as e: logger.debug(f"Erreur lors de la vérification de la plage IP {ip_range}: {e}") return None def _ask_user(self): """ Demande l'environnement à l'utilisateur Returns: str: Type d'environnement choisi """ try: print("\nIziProxy ne peut pas détecter automatiquement l'environnement.") print("Veuillez sélectionner votre environnement:") print("1. Local (développement local)") print("2. Dev (environnement de développement/staging)") print("3. Prod (environnement de production)") choice = input("Votre choix (1/2/3): ") if choice == "1": return "local" elif choice == "2": return "dev" elif choice == "3": return "prod" else: print("Choix non valide, utilisation de 'local' par défaut") return "local" except: # En cas d'erreur (par exemple, exécution sans terminal), utiliser local return "local" def _get_hostname_patterns(self): """ Récupère les motifs de nom d'hôte depuis la configuration Returns: dict: Motifs de nom d'hôte par type d'environnement """ default_patterns = { "local": ["local", "laptop", "desktop", "dev-pc"], "dev": ["dev", "staging", "test", "preprod"], "prod": ["prod", "production"] } if "environment_detection" in self.config and "hostname_patterns" in self.config["environment_detection"]: configured = self.config["environment_detection"]["hostname_patterns"] # Fusionner avec les valeurs par défaut for env_type in self.ENV_TYPES: if env_type in configured: default_patterns[env_type].extend(configured[env_type]) return default_patterns def _get_hostname_regex(self): """ Récupère les expressions régulières de nom d'hôte depuis la configuration Returns: dict: Regex de nom d'hôte par type d'environnement """ default_regex = { "local": ["^laptop-\\w+$", "^pc-\\w+$", "^desktop-\\w+$"], "dev": ["^dev\\d*-", "^staging\\d*-", "^test\\d*-"], "prod": ["^prod\\d*-", "^production\\d*-"] } if "environment_detection" in self.config and "hostname_regex" in self.config["environment_detection"]: configured = self.config["environment_detection"]["hostname_regex"] # Fusionner avec les valeurs par défaut for env_type in self.ENV_TYPES: if env_type in configured: default_regex[env_type].extend(configured[env_type]) return default_regex def _get_ip_ranges(self): """ Récupère les plages IP depuis la configuration Returns: dict: Plages IP par type d'environnement """ if "environment_detection" in self.config and "ip_ranges" in self.config["environment_detection"]: return self.config["environment_detection"]["ip_ranges"] return {} @staticmethod def _ip_in_range(ip, ip_range): """ Vérifie si une IP est dans une plage donnée (CIDR ou plage simple) Args: ip (str): Adresse IP à vérifier ip_range (str): Plage IP (format CIDR "192.168.1.0/24" ou plage "192.168.1.0-192.168.1.255") Returns: bool: True si l'IP est dans la plage """ try: if "/" in ip_range: # Format CIDR from ipaddress import ip_network, ip_address return ip_address(ip) in ip_network(ip_range, strict=False) elif "-" in ip_range: # Format plage start, end = ip_range.split("-") return EnvironmentDetector._ip_to_int(start) <= EnvironmentDetector._ip_to_int(ip) <= EnvironmentDetector._ip_to_int(end) except Exception as e: logger.debug(f"Erreur lors de la vérification de plage IP: {e}") return False return False @staticmethod def _ip_to_int(ip): """ Convertit une adresse IP en entier pour faciliter les comparaisons Args: ip (str): Adresse IP au format string Returns: int: Représentation entière de l'adresse IP """ return sum(int(octet) << (24 - 8 * i) for i, octet in enumerate(ip.split('.')))