120700
This commit is contained in:
482
app_gui.py
Normal file
482
app_gui.py
Normal file
@@ -0,0 +1,482 @@
|
|||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk, scrolledtext, messagebox
|
||||||
|
import threading
|
||||||
|
import queue
|
||||||
|
import paramiko
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
class NetworkConfiguratorApp:
|
||||||
|
def __init__(self, root):
|
||||||
|
self.root = root
|
||||||
|
self.root.title("Network Device Configurator")
|
||||||
|
self.root.geometry("1000x800")
|
||||||
|
self.root.resizable(True, True)
|
||||||
|
|
||||||
|
# Настройка логгирования
|
||||||
|
logging.basicConfig(
|
||||||
|
filename='network_configurator.log',
|
||||||
|
level=logging.INFO,
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Очереди для межпоточного взаимодействия
|
||||||
|
self.log_queue = queue.Queue()
|
||||||
|
self.error_queue = queue.Queue()
|
||||||
|
|
||||||
|
# Списки для хранения данных
|
||||||
|
self.error_ips = []
|
||||||
|
|
||||||
|
# Создаем интерфейс
|
||||||
|
self.create_widgets()
|
||||||
|
|
||||||
|
# Запускаем обработчик очередей
|
||||||
|
self.root.after(100, self.process_queues)
|
||||||
|
|
||||||
|
def create_widgets(self):
|
||||||
|
# Основной фрейм
|
||||||
|
main_frame = ttk.Frame(self.root, padding=10)
|
||||||
|
main_frame.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
|
# Панель с вкладками
|
||||||
|
notebook = ttk.Notebook(main_frame)
|
||||||
|
notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||||
|
|
||||||
|
# Вкладка 1: Учетные данные
|
||||||
|
cred_frame = ttk.Frame(notebook, padding=10)
|
||||||
|
notebook.add(cred_frame, text="Учетные данные")
|
||||||
|
self.create_credentials_tab(cred_frame)
|
||||||
|
|
||||||
|
# Вкладка 2: IP-адреса
|
||||||
|
ip_frame = ttk.Frame(notebook, padding=10)
|
||||||
|
notebook.add(ip_frame, text="IP-адреса")
|
||||||
|
self.create_ip_tab(ip_frame)
|
||||||
|
|
||||||
|
# Вкладка 3: Команды
|
||||||
|
cmd_frame = ttk.Frame(notebook, padding=10)
|
||||||
|
notebook.add(cmd_frame, text="Команды")
|
||||||
|
self.create_commands_tab(cmd_frame)
|
||||||
|
|
||||||
|
# Вкладка 4: Выполнение
|
||||||
|
exec_frame = ttk.Frame(notebook, padding=10)
|
||||||
|
notebook.add(exec_frame, text="Выполнение")
|
||||||
|
self.create_execution_tab(exec_frame)
|
||||||
|
|
||||||
|
# Вкладка 5: Ошибки
|
||||||
|
error_frame = ttk.Frame(notebook, padding=10)
|
||||||
|
notebook.add(error_frame, text="Ошибки")
|
||||||
|
self.create_errors_tab(error_frame)
|
||||||
|
|
||||||
|
# Панель управления
|
||||||
|
control_frame = ttk.Frame(main_frame)
|
||||||
|
control_frame.pack(fill=tk.X, padx=5, pady=5)
|
||||||
|
|
||||||
|
self.start_btn = ttk.Button(control_frame, text="Старт", command=self.start_execution)
|
||||||
|
self.start_btn.pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
self.stop_btn = ttk.Button(control_frame, text="Остановить", command=self.stop_execution, state=tk.DISABLED)
|
||||||
|
self.stop_btn.pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
|
||||||
|
def create_credentials_tab(self, parent):
|
||||||
|
frame = ttk.LabelFrame(parent, text="Учетные данные для подключения")
|
||||||
|
frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||||
|
|
||||||
|
# Логин
|
||||||
|
ttk.Label(frame, text="Логин:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
|
||||||
|
self.username = tk.StringVar()
|
||||||
|
ttk.Entry(frame, textvariable=self.username, width=30).grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)
|
||||||
|
|
||||||
|
# Пароль
|
||||||
|
ttk.Label(frame, text="Пароль:").grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
|
||||||
|
self.password = tk.StringVar()
|
||||||
|
ttk.Entry(frame, textvariable=self.password, show="*", width=30).grid(row=1, column=1, padx=5, pady=5, sticky=tk.W)
|
||||||
|
|
||||||
|
# Кнопка тестового подключения
|
||||||
|
ttk.Button(
|
||||||
|
frame,
|
||||||
|
text="Тестовое подключение",
|
||||||
|
command=self.test_connection
|
||||||
|
).grid(row=2, column=0, columnspan=2, padx=5, pady=10)
|
||||||
|
|
||||||
|
def test_connection(self):
|
||||||
|
"""Тестовое подключение к устройству"""
|
||||||
|
username = self.username.get().strip()
|
||||||
|
password = self.password.get().strip()
|
||||||
|
|
||||||
|
if not username or not password:
|
||||||
|
messagebox.showerror("Ошибка", "Введите логин и пароль")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Получаем первый IP из списка
|
||||||
|
ips = self.ip_text.get("1.0", tk.END).strip().split("\n")
|
||||||
|
ips = [ip.strip() for ip in ips if ip.strip()]
|
||||||
|
|
||||||
|
if not ips:
|
||||||
|
messagebox.showerror("Ошибка", "Введите хотя бы один IP-адрес")
|
||||||
|
return
|
||||||
|
|
||||||
|
test_ip = ips[0]
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.log(f"\nПопытка тестового подключения к {test_ip}...")
|
||||||
|
|
||||||
|
# Создание SSH-клиента
|
||||||
|
client = paramiko.SSHClient()
|
||||||
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
client.connect(
|
||||||
|
test_ip,
|
||||||
|
username=username,
|
||||||
|
password=password,
|
||||||
|
timeout=10,
|
||||||
|
banner_timeout=20
|
||||||
|
)
|
||||||
|
|
||||||
|
# Проверка соединения
|
||||||
|
_, stdout, _ = client.exec_command("show version", timeout=10)
|
||||||
|
output = stdout.read().decode('utf-8', 'ignore')
|
||||||
|
|
||||||
|
self.log(f"Успешное подключение к {test_ip}")
|
||||||
|
self.log(f"Версия ПО:\n{output[:500]}...") # Выводим первые 500 символов
|
||||||
|
|
||||||
|
client.close()
|
||||||
|
messagebox.showinfo("Успех", f"Успешное подключение к {test_ip}")
|
||||||
|
except Exception as e:
|
||||||
|
self.log(f"Ошибка тестового подключения: {str(e)}")
|
||||||
|
messagebox.showerror("Ошибка", f"Не удалось подключиться: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
def create_ip_tab(self, parent):
|
||||||
|
frame = ttk.LabelFrame(parent, text="Список IP-адресов (один на строку)")
|
||||||
|
frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||||
|
|
||||||
|
self.ip_text = scrolledtext.ScrolledText(frame, wrap=tk.WORD, height=10)
|
||||||
|
self.ip_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||||
|
|
||||||
|
# Пример IP-адресов
|
||||||
|
self.ip_text.insert(tk.END, "192.168.1.1\n192.168.1.2\n192.168.1.3")
|
||||||
|
|
||||||
|
|
||||||
|
def create_commands_tab(self, parent):
|
||||||
|
frame = ttk.LabelFrame(parent, text="Команды для выполнения (одна на строку)")
|
||||||
|
frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||||
|
|
||||||
|
self.cmd_text = scrolledtext.ScrolledText(frame, wrap=tk.WORD, height=15)
|
||||||
|
self.cmd_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||||
|
|
||||||
|
# Пример команд
|
||||||
|
example_commands = [
|
||||||
|
"configure terminal",
|
||||||
|
"radius server RS",
|
||||||
|
"address ipv4 10.151.3.66 auth-port 1812 acct-port 1813",
|
||||||
|
"end",
|
||||||
|
"wr"
|
||||||
|
]
|
||||||
|
self.cmd_text.insert(tk.END, "\n".join(example_commands))
|
||||||
|
|
||||||
|
|
||||||
|
def create_execution_tab(self, parent):
|
||||||
|
frame = ttk.LabelFrame(parent, text="Ход выполнения")
|
||||||
|
frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||||
|
|
||||||
|
# Настройки выполнения
|
||||||
|
settings_frame = ttk.Frame(frame)
|
||||||
|
settings_frame.pack(fill=tk.X, padx=5, pady=5)
|
||||||
|
|
||||||
|
ttk.Label(settings_frame, text="Макс. страниц:").pack(side=tk.LEFT, padx=5)
|
||||||
|
self.max_pages = tk.IntVar(value=50)
|
||||||
|
ttk.Entry(settings_frame, textvariable=self.max_pages, width=5).pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
ttk.Label(settings_frame, text="Таймаут (сек):").pack(side=tk.LEFT, padx=5)
|
||||||
|
self.command_timeout = tk.IntVar(value=30)
|
||||||
|
ttk.Entry(settings_frame, textvariable=self.command_timeout, width=5).pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
|
# Логи выполнения
|
||||||
|
self.log_text = scrolledtext.ScrolledText(
|
||||||
|
frame,
|
||||||
|
wrap=tk.WORD,
|
||||||
|
height=20,
|
||||||
|
state=tk.DISABLED
|
||||||
|
)
|
||||||
|
self.log_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||||
|
|
||||||
|
|
||||||
|
def create_errors_tab(self, parent):
|
||||||
|
frame = ttk.LabelFrame(parent, text="Ошибки подключения")
|
||||||
|
frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||||
|
|
||||||
|
# Текст с ошибками
|
||||||
|
self.error_text = scrolledtext.ScrolledText(
|
||||||
|
frame,
|
||||||
|
wrap=tk.WORD,
|
||||||
|
height=10,
|
||||||
|
state=tk.DISABLED
|
||||||
|
)
|
||||||
|
self.error_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||||
|
|
||||||
|
# Кнопка сохранения ошибок
|
||||||
|
btn_frame = ttk.Frame(frame)
|
||||||
|
btn_frame.pack(fill=tk.X, padx=5, pady=5)
|
||||||
|
|
||||||
|
ttk.Button(
|
||||||
|
btn_frame,
|
||||||
|
text="Сохранить ошибки в файл",
|
||||||
|
command=self.save_errors
|
||||||
|
).pack(side=tk.RIGHT, padx=5)
|
||||||
|
|
||||||
|
|
||||||
|
def log(self, message):
|
||||||
|
"""Добавление сообщения в очередь логов"""
|
||||||
|
self.log_queue.put(message)
|
||||||
|
|
||||||
|
|
||||||
|
def log_error(self, ip, message):
|
||||||
|
"""Добавление ошибки в очередь ошибок"""
|
||||||
|
self.error_queue.put((ip, message))
|
||||||
|
|
||||||
|
|
||||||
|
def process_queues(self):
|
||||||
|
"""Обработка сообщений из очередей"""
|
||||||
|
# Обработка логов
|
||||||
|
while not self.log_queue.empty():
|
||||||
|
message = self.log_queue.get_nowait()
|
||||||
|
self.log_text.config(state=tk.NORMAL)
|
||||||
|
self.log_text.insert(tk.END, message + "\n")
|
||||||
|
self.log_text.config(state=tk.DISABLED)
|
||||||
|
self.log_text.see(tk.END)
|
||||||
|
|
||||||
|
# Обработка ошибок
|
||||||
|
while not self.error_queue.empty():
|
||||||
|
ip, error = self.error_queue.get_nowait()
|
||||||
|
self.error_ips.append((ip, error))
|
||||||
|
|
||||||
|
self.error_text.config(state=tk.NORMAL)
|
||||||
|
self.error_text.insert(tk.END, f"{ip}: {error}\n")
|
||||||
|
self.error_text.config(state=tk.DISABLED)
|
||||||
|
self.error_text.see(tk.END)
|
||||||
|
|
||||||
|
# Продолжаем обработку
|
||||||
|
self.root.after(100, self.process_queues)
|
||||||
|
|
||||||
|
|
||||||
|
def start_execution(self):
|
||||||
|
"""Запуск выполнения команд"""
|
||||||
|
# Получаем учетные данные
|
||||||
|
username = self.username.get().strip()
|
||||||
|
password = self.password.get().strip()
|
||||||
|
|
||||||
|
if not username or not password:
|
||||||
|
messagebox.showerror("Ошибка", "Введите логин и пароль")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Получаем IP-адреса
|
||||||
|
ips = self.ip_text.get("1.0", tk.END).strip().split("\n")
|
||||||
|
ips = [ip.strip() for ip in ips if ip.strip()]
|
||||||
|
|
||||||
|
if not ips:
|
||||||
|
messagebox.showerror("Ошибка", "Введите хотя бы один IP-адрес")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Получаем команды
|
||||||
|
commands = self.cmd_text.get("1.0", tk.END).strip().split("\n")
|
||||||
|
commands = [cmd.strip() for cmd in commands if cmd.strip()]
|
||||||
|
|
||||||
|
if not commands:
|
||||||
|
messagebox.showerror("Ошибка", "Введите команды для выполнения")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Очищаем логи и ошибки
|
||||||
|
self.log_text.config(state=tk.NORMAL)
|
||||||
|
self.log_text.delete("1.0", tk.END)
|
||||||
|
self.log_text.config(state=tk.DISABLED)
|
||||||
|
|
||||||
|
self.error_text.config(state=tk.NORMAL)
|
||||||
|
self.error_text.delete("1.0", tk.END)
|
||||||
|
self.error_text.config(state=tk.DISABLED)
|
||||||
|
|
||||||
|
self.error_ips = []
|
||||||
|
|
||||||
|
# Меняем состояние кнопок
|
||||||
|
self.start_btn.config(state=tk.DISABLED)
|
||||||
|
self.stop_btn.config(state=tk.NORMAL)
|
||||||
|
|
||||||
|
# Запускаем выполнение в отдельном потоке
|
||||||
|
self.stop_event = threading.Event()
|
||||||
|
threading.Thread(
|
||||||
|
target=self.execute_commands,
|
||||||
|
args=(ips, username, password, commands),
|
||||||
|
daemon=True
|
||||||
|
).start()
|
||||||
|
|
||||||
|
|
||||||
|
def stop_execution(self):
|
||||||
|
"""Остановка выполнения команд"""
|
||||||
|
if hasattr(self, 'stop_event'):
|
||||||
|
self.stop_event.set()
|
||||||
|
self.log("Выполнение остановлено пользователем")
|
||||||
|
|
||||||
|
self.start_btn.config(state=tk.NORMAL)
|
||||||
|
self.stop_btn.config(state=tk.DISABLED)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_commands(self, ips, username, password, commands):
|
||||||
|
"""Выполнение команд на устройствах с обработкой --More--"""
|
||||||
|
|
||||||
|
|
||||||
|
self.log(f"Начато выполнение для {len(ips)} устройств")
|
||||||
|
|
||||||
|
# Различные варианты промпта "More"
|
||||||
|
more_prompts = [
|
||||||
|
"--More--",
|
||||||
|
" -- More -- ",
|
||||||
|
"-More-",
|
||||||
|
"Press any key to continue",
|
||||||
|
"続けるには何かキーを押してください . . ."
|
||||||
|
]
|
||||||
|
|
||||||
|
# Стандартные промпты для определения конца вывода
|
||||||
|
end_prompts = [">", "#", "$", ":"]
|
||||||
|
|
||||||
|
for ip in ips:
|
||||||
|
if self.stop_event.is_set():
|
||||||
|
self.log("Выполнение прервано")
|
||||||
|
break
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.log(f"\nПодключение к {ip}...")
|
||||||
|
|
||||||
|
# Создание SSH-клиента
|
||||||
|
client = paramiko.SSHClient()
|
||||||
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
client.connect(
|
||||||
|
ip,
|
||||||
|
username=username,
|
||||||
|
password=password,
|
||||||
|
timeout=10,
|
||||||
|
banner_timeout=20
|
||||||
|
)
|
||||||
|
|
||||||
|
# Открытие интерактивного сеанса
|
||||||
|
shell = client.invoke_shell()
|
||||||
|
time.sleep(1) # Даем время на установление сессии
|
||||||
|
|
||||||
|
# Получаем начальный вывод (приглашение)
|
||||||
|
initial_output = ""
|
||||||
|
start_time = time.time()
|
||||||
|
while time.time() - start_time < 5: # Таймаут 5 секунд
|
||||||
|
if shell.recv_ready():
|
||||||
|
initial_output += shell.recv(4096).decode('utf-8', 'ignore')
|
||||||
|
else:
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
# Выполнение команд
|
||||||
|
for command in commands:
|
||||||
|
if self.stop_event.is_set():
|
||||||
|
break
|
||||||
|
|
||||||
|
# Отправка команды
|
||||||
|
shell.send(command + "\n")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# Сбор вывода с обработкой --More--
|
||||||
|
output = ""
|
||||||
|
max_pages = self.max_pages.get()
|
||||||
|
page_count = 0
|
||||||
|
end_detected = False
|
||||||
|
start_time = time.time()
|
||||||
|
|
||||||
|
while not end_detected and time.time() - start_time < 30: # Таймаут 30 секунд на команду
|
||||||
|
if self.stop_event.is_set():
|
||||||
|
break
|
||||||
|
|
||||||
|
# Проверяем, есть ли данные для чтения
|
||||||
|
if shell.recv_ready():
|
||||||
|
chunk = shell.recv(4096).decode('utf-8', 'ignore')
|
||||||
|
output += chunk
|
||||||
|
|
||||||
|
# Проверяем наличие промпта More
|
||||||
|
if any(prompt in chunk for prompt in more_prompts):
|
||||||
|
# Симулируем нажатие пробела
|
||||||
|
shell.send(" ")
|
||||||
|
time.sleep(0.5)
|
||||||
|
page_count += 1
|
||||||
|
self.log(f"[{ip}] Обнаружен промпт More, отправка пробела ({page_count}/{max_pages})")
|
||||||
|
|
||||||
|
# Прерываем если слишком много страниц
|
||||||
|
if page_count > max_pages:
|
||||||
|
self.log(f"[{ip}] Превышено максимальное количество страниц для команды: {command}")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# Проверяем, закончился ли вывод (по приглашению)
|
||||||
|
if any(prompt in output for prompt in end_prompts):
|
||||||
|
end_detected = True
|
||||||
|
else:
|
||||||
|
# Если данных нет, делаем небольшую паузу
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# Проверяем завершение по таймауту
|
||||||
|
if time.time() - start_time > 5: # 5 секунд без данных
|
||||||
|
break
|
||||||
|
|
||||||
|
# Логируем команду и вывод
|
||||||
|
self.log(f"[{ip}] >>> {command}")
|
||||||
|
self.log(f"[{ip}] <<< {output}")
|
||||||
|
|
||||||
|
# Проверка на ошибки
|
||||||
|
error_checks = [
|
||||||
|
("Password:", "Запрос пароля (возможно, неверный уровень доступа)"),
|
||||||
|
("Authentication failed", "Сбой аутентификации на устройстве"),
|
||||||
|
("Invalid input", f"Неверная команда: '{command}'"),
|
||||||
|
("Connection timed out", "Таймаут соединения"),
|
||||||
|
("Error", f"Ошибка выполнения команды: {output.strip()}"),
|
||||||
|
("Failed", f"Ошибка выполнения команды: {output.strip()}")
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern, message in error_checks:
|
||||||
|
if pattern in output:
|
||||||
|
self.log_error(ip, message)
|
||||||
|
break
|
||||||
|
|
||||||
|
self.log(f"Выполнение команд на {ip} завершено")
|
||||||
|
except paramiko.AuthenticationException:
|
||||||
|
self.log_error(ip, "Ошибка аутентификации: неверные учетные данные")
|
||||||
|
except paramiko.SSHException as e:
|
||||||
|
self.log_error(ip, f"Ошибка SSH: {str(e)}")
|
||||||
|
except Exception as e:
|
||||||
|
self.log_error(ip, f"Ошибка подключения: {str(e)}")
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
client.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.log("\nВыполнение завершено")
|
||||||
|
self.start_btn.config(state=tk.NORMAL)
|
||||||
|
self.stop_btn.config(state=tk.DISABLED)
|
||||||
|
|
||||||
|
|
||||||
|
def save_errors(self):
|
||||||
|
"""Сохранение ошибок в файл"""
|
||||||
|
if not self.error_ips:
|
||||||
|
messagebox.showinfo("Информация", "Нет ошибок для сохранения")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open("network_errors.txt", "w", encoding="utf-8") as f:
|
||||||
|
f.write("Список устройств с ошибками подключения:\n\n")
|
||||||
|
for ip, error in self.error_ips:
|
||||||
|
f.write(f"{ip}: {error}\n")
|
||||||
|
|
||||||
|
self.log("Ошибки сохранены в файл: network_errors.txt")
|
||||||
|
messagebox.showinfo("Успех", "Ошибки сохранены в файл network_errors.txt")
|
||||||
|
except Exception as e:
|
||||||
|
self.log(f"Ошибка при сохранении файла: {str(e)}")
|
||||||
|
messagebox.showerror("Ошибка", f"Не удалось сохранить файл: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
root = tk.Tk()
|
||||||
|
app = NetworkConfiguratorApp(root)
|
||||||
|
root.mainloop()
|
||||||
Reference in New Issue
Block a user