229 lines
8.0 KiB
Python
229 lines
8.0 KiB
Python
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
|