""" Module de test réseau - Effectue divers tests de connectivité et de performance """ import subprocess import time import requests import socket import json import threading from dataclasses import dataclass from typing import Optional, Dict, Any from datetime import datetime # Import optionnel de speedtest try: import speedtest SPEEDTEST_AVAILABLE = True except ImportError: SPEEDTEST_AVAILABLE = False speedtest = None # Import optionnel de ping3 try: import ping3 PING3_AVAILABLE = True except ImportError: PING3_AVAILABLE = False ping3 = None @dataclass class TestResult: """Résultat d'un test réseau""" timestamp: datetime test_type: str success: bool latency_ms: Optional[float] = None download_speed: Optional[float] = None # en Mbps upload_speed: Optional[float] = None # en Mbps packet_loss: Optional[float] = None # en % jitter: Optional[float] = None # en ms error_message: Optional[str] = None server: Optional[str] = None def to_dict(self) -> Dict[str, Any]: return { 'timestamp': self.timestamp.isoformat(), 'test_type': self.test_type, 'success': self.success, 'latency_ms': self.latency_ms, 'download_speed': self.download_speed, 'upload_speed': self.upload_speed, 'packet_loss': self.packet_loss, 'jitter': self.jitter, 'error_message': self.error_message, 'server': self.server } class NetworkTester: """ Classe pour effectuer divers tests de connectivité et de performance réseau """ # Serveurs de test par défaut PING_SERVERS = [ "8.8.8.8", # Google DNS "1.1.1.1", # Cloudflare DNS "9.9.9.9", # Quad9 DNS "8.8.4.4", # Google DNS secondaire "208.67.222.222" # OpenDNS ] SPEEDTEST_SERVERS = [ {"name": "Paris", "id": 12345}, # ID à adapter selon votre région {"name": "Lyon", "id": 67890}, ] # URL pour tester le téléchargement DOWNLOAD_TEST_URL = "https://speedtest.ftp.otenet.gr/files/test1Mb.db" UPLOAD_TEST_URL = "https://httpbin.org/post" def __init__(self, timeout: int = 5): """ Initialisation du testeur réseau Args: timeout: Timeout en secondes pour les tests """ self.timeout = timeout self.last_test_time = None def ping_test(self, server: str = None, count: int = 4) -> TestResult: """ Effectue un test de ping vers un serveur Args: server: Adresse IP ou hostname à pinger (par défaut, utilise le premier serveur) count: Nombre de paquets à envoyer Returns: TestResult: Résultat du test """ if server is None: server = self.PING_SERVERS[0] if not PING3_AVAILABLE: return TestResult( timestamp=datetime.now(), test_type="ping", success=False, error_message="ping3 non installé. Installez avec: pip install ping3" ) try: # Utilisation de ping3 pour plus de fiabilité start_time = time.time() # Effectuer le ping latency_ms = ping3.ping(server, unit='ms', timeout=self.timeout) if latency_ms is None or latency_ms is False: return TestResult( timestamp=datetime.now(), test_type="ping", success=False, error_message=f"Échec du ping vers {server}", server=server ) # Calcul du jitter (variation de latence) latencies = [] for _ in range(count): lat = ping3.ping(server, unit='ms', timeout=self.timeout) if lat is not None and lat is not False: latencies.append(lat) jitter = max(latencies) - min(latencies) if latencies else 0 return TestResult( timestamp=datetime.now(), test_type="ping", success=True, latency_ms=latency_ms, jitter=jitter, server=server ) except Exception as e: return TestResult( timestamp=datetime.now(), test_type="ping", success=False, error_message=f"Erreur lors du ping: {str(e)}", server=server ) def ping_all_servers(self) -> list: """ Ping tous les serveurs configurés Returns: Liste de TestResult pour chaque serveur """ results = [] for server in self.PING_SERVERS: result = self.ping_test(server, count=2) results.append(result) time.sleep(0.5) # Petite pause entre les tests return results def connection_test(self, url: str = "https://www.google.com") -> TestResult: """ Teste la connectivité HTTP vers une URL Args: url: URL à tester Returns: TestResult: Résultat du test """ try: start_time = time.time() response = requests.get(url, timeout=self.timeout) latency = (time.time() - start_time) * 1000 # en ms if response.status_code < 400: return TestResult( timestamp=datetime.now(), test_type="http_connection", success=True, latency_ms=latency, server=url ) else: return TestResult( timestamp=datetime.now(), test_type="http_connection", success=False, error_message=f"Code HTTP: {response.status_code}", server=url ) except requests.exceptions.RequestException as e: return TestResult( timestamp=datetime.now(), test_type="http_connection", success=False, error_message=f"Erreur de connexion: {str(e)}", server=url ) def dns_test(self, domain: str = "google.com") -> TestResult: """ Teste la résolution DNS Args: domain: Domaine à résoudre Returns: TestResult: Résultat du test """ try: start_time = time.time() # Résolution DNS ip_addresses = socket.getaddrinfo(domain, None) latency = (time.time() - start_time) * 1000 if ip_addresses: return TestResult( timestamp=datetime.now(), test_type="dns_resolution", success=True, latency_ms=latency, server=domain ) else: return TestResult( timestamp=datetime.now(), test_type="dns_resolution", success=False, error_message="Aucune adresse IP trouvée", server=domain ) except socket.gaierror as e: return TestResult( timestamp=datetime.now(), test_type="dns_resolution", success=False, error_message=f"Erreur DNS: {str(e)}", server=domain ) def download_speed_test(self, url: str = None, duration: int = 5) -> TestResult: """ Teste la vitesse de téléchargement Args: url: URL du fichier à télécharger duration: Durée du test en secondes Returns: TestResult: Résultat du test """ if url is None: url = self.DOWNLOAD_TEST_URL try: start_time = time.time() response = requests.get(url, stream=True, timeout=self.timeout) # Lire les données par blocs pour mesurer la vitesse bytes_received = 0 chunk_size = 1024 * 1024 # 1 Mo while time.time() - start_time < duration: chunk = response.raw.read(chunk_size) if not chunk: break bytes_received += len(chunk) elapsed = time.time() - start_time speed_mbps = (bytes_received * 8) / (1024 * 1024) / elapsed if elapsed > 0 else 0 return TestResult( timestamp=datetime.now(), test_type="download_speed", success=True, download_speed=round(speed_mbps, 2), server=url ) except Exception as e: return TestResult( timestamp=datetime.now(), test_type="download_speed", success=False, error_message=f"Erreur de téléchargement: {str(e)}", server=url ) def upload_speed_test(self, url: str = None, data_size: int = 1024 * 1024) -> TestResult: """ Teste la vitesse d'upload Args: url: URL pour le test d'upload data_size: Taille des données à uploader en octets Returns: TestResult: Résultat du test """ if url is None: url = self.UPLOAD_TEST_URL try: # Créer des données aléatoires data = b'0' * data_size start_time = time.time() response = requests.post(url, data=data, timeout=self.timeout * 2) elapsed = time.time() - start_time speed_mbps = (data_size * 8) / (1024 * 1024) / elapsed if elapsed > 0 else 0 if response.status_code < 400: return TestResult( timestamp=datetime.now(), test_type="upload_speed", success=True, upload_speed=round(speed_mbps, 2), server=url ) else: return TestResult( timestamp=datetime.now(), test_type="upload_speed", success=False, error_message=f"Code HTTP: {response.status_code}", server=url ) except Exception as e: return TestResult( timestamp=datetime.now(), test_type="upload_speed", success=False, error_message=f"Erreur d'upload: {str(e)}", server=url ) def full_speed_test(self) -> TestResult: """ Effectue un test complet de vitesse (download + upload) avec speedtest-cli Returns: TestResult: Résultat du test """ if not SPEEDTEST_AVAILABLE: return TestResult( timestamp=datetime.now(), test_type="full_speed_test", success=False, error_message="speedtest-cli non installé. Installez avec: pip install speedtest-cli" ) try: st = speedtest.Speedtest() # Sélection du meilleur serveur st.get_best_server() # Test de téléchargement st.download() download_speed = st.results.dict()["download"] / 1024 / 1024 # en Mbps # Test d'upload st.upload() upload_speed = st.results.dict()["upload"] / 1024 / 1024 # en Mbps # Latence (ping) latency = st.results.dict()["ping"] return TestResult( timestamp=datetime.now(), test_type="full_speed_test", success=True, latency_ms=latency, download_speed=round(download_speed, 2), upload_speed=round(upload_speed, 2), server=st.results.dict()["server"]["name"] ) except Exception as e: return TestResult( timestamp=datetime.now(), test_type="full_speed_test", success=False, error_message=f"Erreur lors du speedtest: {str(e)}" ) def comprehensive_test(self) -> Dict[str, TestResult]: """ Effectue une série complète de tests réseau Returns: Dictionnaire avec les résultats de tous les tests """ results = {} # Test de connectivité results['connection'] = self.connection_test() # Test DNS results['dns'] = self.dns_test() # Tests de ping results['ping_google'] = self.ping_test("8.8.8.8") results['ping_cloudflare'] = self.ping_test("1.1.1.1") # Test de vitesse de téléchargement (rapide) results['download'] = self.download_speed_test(duration=3) # Test de vitesse d'upload (rapide) results['upload'] = self.upload_speed_test(data_size=512 * 1024) return results