import socket import subprocess import time from datetime import datetime from typing import Dict, List, Tuple, Optional from PyQt6.QtCore import QThread, pyqtSignal class ConnectivityTestThread(QThread): """Thread para ejecutar tests de conectividad en paralelo.""" test_result = pyqtSignal(dict) # Emite resultado de cada test all_finished = pyqtSignal() # Emite cuando todos terminan def __init__(self, tests: List[Dict]): super().__init__() self.tests = tests self.results = [] def run(self): for test in self.tests: result = self._run_test(test) self.results.append(result) self.test_result.emit(result) self.all_finished.emit() def _run_test(self, test: Dict) -> Dict: """Ejecuta un test individual y retorna resultado.""" host = test["host"] port = test["port"] protocol = test.get("protocol", "TCP") test_type = test.get("type", "connectivity") result = { "host": host, "port": port, "protocol": protocol, "type": test_type, "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "success": False, "status": "Unknown", "latency_ms": None, "error": None, "details": "", } try: if protocol == "TCP": result.update(self._test_tcp(host, port)) elif protocol == "UDP": if test_type == "ntp": result.update(self._test_ntp(host)) else: result.update(self._test_udp(host, port)) else: result["error"] = f"Protocolo desconocido: {protocol}" except Exception as e: result["error"] = str(e) result["status"] = "Error" return result def _test_tcp(self, host: str, port: int) -> Dict: """Test de conectividad TCP.""" start_time = time.time() try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) result = sock.connect_ex((host, port)) latency = (time.time() - start_time) * 1000 sock.close() if result == 0: return { "success": True, "status": "Open", "latency_ms": round(latency, 2), "details": f"Conexión TCP exitosa en {latency:.2f}ms", } else: return { "success": False, "status": "Closed/Filtered", "latency_ms": None, "details": f"No se pudo conectar (código: {result})", } except socket.timeout: return { "success": False, "status": "Timeout", "latency_ms": None, "details": "Timeout esperando respuesta", } except Exception as e: return { "success": False, "status": "Error", "latency_ms": None, "error": str(e), "details": f"Error: {str(e)}", } def _test_udp(self, host: str, port: int) -> Dict: """Test básico UDP (envía paquete y espera respuesta).""" try: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(3) start_time = time.time() # Enviar paquete vacío sock.sendto(b"", (host, port)) try: data, addr = sock.recvfrom(1024) latency = (time.time() - start_time) * 1000 sock.close() return { "success": True, "status": "Open", "latency_ms": round(latency, 2), "details": f"Respuesta UDP recibida en {latency:.2f}ms", } except socket.timeout: sock.close() return { "success": False, "status": "Open|Filtered", "latency_ms": None, "details": "No respuesta (puerto puede estar abierto pero filtrado)", } except Exception as e: return { "success": False, "status": "Error", "latency_ms": None, "error": str(e), "details": f"Error: {str(e)}", } def _test_ntp(self, host: str) -> Dict: """Test NTP específico (puerto UDP 123).""" try: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(5) start_time = time.time() # Paquete NTP básico (modo 3: cliente) ntp_packet = bytearray(48) ntp_packet[0] = 0x1B # LI=0, VN=3, Mode=3 sock.sendto(ntp_packet, (host, 123)) try: data, addr = sock.recvfrom(1024) latency = (time.time() - start_time) * 1000 if len(data) >= 48: # Extraer timestamp NTP try: seconds = ( (data[40] << 24) | (data[41] << 16) | (data[42] << 8) | data[43] ) # NTP epoch: 1900-01-01 ntp_epoch = datetime(1900, 1, 1) ntp_time = ntp_epoch.timestamp() + seconds ntp_dt = datetime.fromtimestamp(ntp_time) time_str = ntp_dt.strftime('%Y-%m-%d %H:%M:%S') except (ValueError, OverflowError, OSError): time_str = "Timestamp no válido" sock.close() return { "success": True, "status": "Open", "latency_ms": round(latency, 2), "details": f"NTP hora local: {time_str}", } else: sock.close() return { "success": False, "status": "Error", "latency_ms": None, "details": "Respuesta NTP incompleta", } except socket.timeout: sock.close() return { "success": False, "status": "Timeout", "latency_ms": None, "details": "Timeout esperando respuesta NTP", } except Exception as e: return { "success": False, "status": "Error", "latency_ms": None, "error": str(e), "details": f"Error NTP: {str(e)}", } class VLAN35Checker: """Gestor de tests de conectividad para VLAN 35.""" def __init__(self): # Configuración por defecto basada en el script .bat self.default_tests = [ {"host": "10.47.8.133", "port": 515, "protocol": "TCP", "type": "connectivity"}, {"host": "10.47.8.133", "port": 9195, "protocol": "TCP", "type": "connectivity"}, {"host": "10.47.8.135", "port": 9191, "protocol": "TCP", "type": "connectivity"}, {"host": "10.47.8.135", "port": 9192, "protocol": "TCP", "type": "connectivity"}, {"host": "10.240.225.171", "port": 465, "protocol": "TCP", "type": "connectivity"}, {"host": "10.244.255.250", "port": 123, "protocol": "UDP", "type": "ntp"}, ] def get_default_tests(self) -> List[Dict]: """Retorna la lista de tests por defecto.""" return self.default_tests.copy() def run_tests(self, tests: List[Dict]) -> ConnectivityTestThread: """Inicia los tests en un thread.""" thread = ConnectivityTestThread(tests) thread.start() return thread