Добавить main.py
This commit is contained in:
@@ -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()
|
||||
Reference in New Issue
Block a user