Добавить main.py

This commit is contained in:
2026-08-03 14:46:27 +00:00
parent 7b94b2697e
commit 1d4550d748
+383
View File
@@ -0,0 +1,383 @@
#!/usr/bin/env python3
"""
Скрипт для анализа VLAN на множестве роутеров OpenWrt.
Для каждого роутера:
1. Находит VLAN с наибольшим количеством портов
2. Отключает все порты в нем (кроме 6t)
3. Запускает iperf3 -c 172.20.100.203 --bidir на роутере
4. Сохраняет результат в отдельный файл
5. Включает порты обратно
"""
import paramiko
import re
import sys
import time
import datetime
import os
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
class RouterTester:
def __init__(self, host, username, password, port=22):
self.host = host
self.username = username
self.password = password
self.port = port
self.ssh = None
self.vlan_info = None
self.ports_toggled = []
def connect(self):
"""Подключается к роутеру по SSH."""
try:
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh.connect(
self.host,
port=self.port,
username=self.username,
password=self.password,
timeout=10
)
return True
except Exception as e:
print(f"{self.host}: Ошибка подключения: {e}")
return False
def close(self):
"""Закрывает SSH-соединение."""
if self.ssh:
self.ssh.close()
def exec_command(self, command):
"""Выполняет команду на роутере и возвращает вывод."""
stdin, stdout, stderr = self.ssh.exec_command(command)
output = stdout.read().decode('utf-8')
error = stderr.read().decode('utf-8')
return output, error
def get_switch_config(self):
"""Получает конфигурацию коммутатора."""
output, error = self.exec_command('swconfig dev switch0 show')
if error:
print(f"{self.host}: Ошибка swconfig: {error}")
return None
return output
def parse_vlans(self, config_text):
"""Парсит VLAN из вывода swconfig."""
vlans = []
current_vlan = None
lines = config_text.split('\n')
for line in lines:
line = line.strip()
vlan_match = re.match(r'^VLAN\s+(\d+):$', line)
if vlan_match:
if current_vlan:
vlans.append(current_vlan)
current_vlan = {'vid': None, 'ports': [], 'vlan_num': int(vlan_match.group(1))}
continue
if current_vlan is not None:
vid_match = re.match(r'^vid:\s+(\d+)$', line)
if vid_match:
current_vlan['vid'] = int(vid_match.group(1))
continue
ports_match = re.match(r'^ports:\s+(.+)$', line)
if ports_match:
ports_str = ports_match.group(1)
current_vlan['ports'] = [p.strip() for p in ports_str.split() if p.strip()]
continue
if current_vlan and current_vlan.get('vid') is not None:
vlans.append(current_vlan)
return vlans
def find_vlan_with_most_ports(self, vlans):
"""Находит VLAN с максимальным количеством портов."""
if not vlans:
return None
return sorted(vlans, key=lambda v: len(v['ports']), reverse=True)[0]
def get_ports_to_toggle(self, ports):
"""Возвращает порты для отключения (исключая 6t)."""
return [p for p in ports if p != '6t']
def disable_ports(self, ports):
"""Отключает указанные порты."""
print(f" {self.host}: Отключаю порты: {' '.join(ports)}")
for port in ports:
port_num = port.replace('t', '')
command = f'swconfig dev switch0 port {port_num} set port_disabled 1'
self.exec_command(command)
self.exec_command('swconfig dev switch0 set apply')
self.ports_toggled = ports
time.sleep(1)
def enable_ports(self):
"""Включает порты обратно."""
if not self.ports_toggled:
return
print(f" {self.host}: Включаю порты: {' '.join(self.ports_toggled)}")
for port in self.ports_toggled:
port_num = port.replace('t', '')
command = f'swconfig dev switch0 port {port_num} set port_disabled 0'
self.exec_command(command)
self.exec_command('swconfig dev switch0 set apply')
time.sleep(1)
def run_iperf_test(self, server_ip, output_file, duration=10, port=5201):
"""
Запускает iperf3 -c на роутере с флагом --bidir.
"""
print(f" {self.host}: Запуск iperf3 -c {server_ip} --bidir (duration={duration}с)")
# Временный файл на роутере
temp_file = '/tmp/iperf3_result.log'
# Команда с флагом --bidir
command = f'iperf3 -c {server_ip} -t {duration} -p {port} --bidir > {temp_file} 2>&1'
# Запускаем iperf3 в фоновом режиме
self.exec_command(command)
# Ждем завершения теста
time.sleep(duration + 3)
# Проверяем, завершился ли процесс
output, _ = self.exec_command('ps aux | grep "iperf3 -c" | grep -v grep')
if output.strip():
print(f" {self.host}: ⚠️ iperf3 все еще работает, принудительно останавливаю...")
self.exec_command('pkill -f "iperf3 -c"')
time.sleep(1)
# Читаем результат
output, error = self.exec_command(f'cat {temp_file}')
self.exec_command(f'rm -f {temp_file}')
if error:
print(f" {self.host}: ❌ Ошибка при чтении результата: {error}")
return False
# Сохраняем результат
try:
with open(output_file, 'w') as f:
f.write(f"=== iperf3 тест (bidir) ===\n")
f.write(f"Роутер: {self.host}\n")
f.write(f"Сервер: {server_ip}:{port}\n")
f.write(f"Режим: bidirectional (--bidir)\n")
f.write(f"Длительность: {duration} сек\n")
f.write(f"Время запуска: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write("=" * 50 + "\n\n")
f.write(output)
# Проверяем наличие ошибок в выводе
if "error" in output.lower() or "failed" in output.lower():
print(f" {self.host}: ⚠️ iperf3 завершился с ошибкой")
return False
print(f" {self.host}: ✅ iperf3 тест завершен успешно")
return True
except Exception as e:
print(f" {self.host}: ❌ Ошибка при сохранении результата: {e}")
return False
def check_iperf3(self):
"""Проверяет наличие iperf3 на роутере."""
output, _ = self.exec_command('which iperf3')
return bool(output.strip())
def process(self, server_ip, output_dir, duration=10, port=5201):
"""
Полный процесс обработки одного роутера.
"""
print(f"\n{'=' * 50}")
print(f"🔍 Обработка роутера: {self.host}")
print(f"{'=' * 50}")
# Подключаемся
if not self.connect():
return False
try:
# Проверяем iperf3
if not self.check_iperf3():
print(f" {self.host}: ❌ iperf3 не установлен")
print(f" {self.host}: Установите: opkg update && opkg install iperf3")
return False
# Получаем конфигурацию VLAN
config = self.get_switch_config()
if config is None:
return False
# Парсим VLAN
vlans = self.parse_vlans(config)
if not vlans:
print(f" {self.host}: ❌ VLAN не найдены")
return False
# Находим VLAN с наибольшим количеством портов
best_vlan = self.find_vlan_with_most_ports(vlans)
print(f" {self.host}: Выбран VLAN {best_vlan['vlan_num']} (vid: {best_vlan['vid']})")
print(f" {self.host}: Порты: {' '.join(best_vlan['ports'])}")
# Определяем порты для отключения
ports_to_toggle = self.get_ports_to_toggle(best_vlan['ports'])
if not ports_to_toggle:
print(f" {self.host}: ⚠️ Нет портов для отключения (только 6t)")
return False
print(f" {self.host}: Будут отключены порты: {' '.join(ports_to_toggle)}")
# Генерируем имя файла
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
output_file = os.path.join(output_dir, f'iperf3_{self.host}_{timestamp}.log')
# Шаг 1: Отключаем порты
print(f" {self.host}: --- Шаг 1: Отключение портов ---")
self.disable_ports(ports_to_toggle)
# Шаг 2: Запускаем iperf3 с --bidir
print(f" {self.host}: --- Шаг 2: iperf3 тест ---")
success = self.run_iperf_test(server_ip, output_file, duration, port)
# Шаг 3: Включаем порты обратно
print(f" {self.host}: --- Шаг 3: Включение портов ---")
self.enable_ports()
return success
except Exception as e:
print(f" {self.host}: ❌ Критическая ошибка: {e}")
# Пытаемся включить порты в случае ошибки
try:
self.enable_ports()
except:
pass
return False
finally:
self.close()
def main():
# Настройки iperf3
IPERF_SERVER = '172.20.100.203'
IPERF_PORT = 5201
IPERF_DURATION = 10
# Директория для результатов
OUTPUT_DIR = './iperf_results'
if not os.path.exists(OUTPUT_DIR):
os.makedirs(OUTPUT_DIR)
# Список роутеров
routers = [
#{'host': '192.168.16.4', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.8', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.9', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.10', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.11', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.27', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.28', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.29', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.30', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.32', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.34', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.35', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.38', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.39', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.40', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.41', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.42', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.43', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.44', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.45', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.49', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.50', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.51', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.55', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.57', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.58', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.62', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.65', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.66', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.67', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.68', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.69', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.70', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.76', 'username': 'root', 'password': 'root'},
#{'host': '192.168.16.81', 'username': 'root', 'password': 'root'},
{'host': '192.168.16.82', 'username': 'root', 'password': 'root'},
{'host': '192.168.16.84', 'username': 'root', 'password': 'root'},
]
print(f"🚀 Запуск тестирования {len(routers)} роутеров")
print(f"📡 iperf3 сервер: {IPERF_SERVER}:{IPERF_PORT}")
print(f"⏱️ Длительность теста: {IPERF_DURATION} сек")
print(f"📁 Результаты сохраняются в: {OUTPUT_DIR}")
print("=" * 60)
# Статистика
success_count = 0
fail_count = 0
results = []
# Обрабатываем роутеры последовательно
for i, router_config in enumerate(routers, 1):
print(f"\n📌 [{i}/{len(routers)}] Обработка роутера {router_config['host']}")
tester = RouterTester(
router_config['host'],
router_config['username'],
router_config['password']
)
success = tester.process(
IPERF_SERVER,
OUTPUT_DIR,
duration=IPERF_DURATION,
port=IPERF_PORT
)
if success:
success_count += 1
results.append(f"{router_config['host']}: УСПЕШНО")
else:
fail_count += 1
results.append(f"{router_config['host']}: ОШИБКА")
# Небольшая пауза между роутерами
if i < len(routers):
print(f"\n⏳ Пауза 3 секунды перед следующим роутером...")
time.sleep(3)
# Итоговая статистика
print("\n" + "=" * 60)
print("📊 ИТОГОВАЯ СТАТИСТИКА")
print("=" * 60)
for result in results:
print(result)
print("-" * 60)
print(f"✅ Успешно: {success_count}")
print(f"❌ Ошибок: {fail_count}")
print(f"📁 Все результаты сохранены в: {OUTPUT_DIR}")
print("=" * 60)
if __name__ == '__main__':
main()