#!/usr/bin/env python3 import tkinter as tk from tkinter import messagebox import tkinter.ttk as ttk import urllib.request import json import tempfile import subprocess import sys import os import threading from urllib.parse import urljoin # Список серверов SERVERS = [ {"name": "Гринатом (МСК)", "url": "http://gren-s-synd01.gk.rosatom.local/admin_helper/"}, {"name": "Гринатом (НН)", "url": "http://grenn-s-synd01.gk.rosatom.local/admin_helper/"}, {"name": "ГК Росатом", "url": "http://gkrs-s-synd01.gk.rosatom.local/admin_helper/"}, ] # Имя файла описания (manifest) MANIFEST_FILE = "scripts.json" class AdminHelper: def __init__(self, root): self.root = root self.root.title("Admin Helper (Linux)") self.root.geometry("500x450") # --- Верхняя панель (Выпадающий список) --- self.server_frame = tk.Frame(root, pady=10, bg="#f0f0f0") self.server_frame.pack(fill='x') tk.Label(self.server_frame, text="Сервер:", bg="#f0f0f0", font=("Arial", 10, "bold")).pack(side='left', padx=5) # Создаем выпадающий список server_names = [s['name'] for s in SERVERS] self.server_combo = ttk.Combobox( self.server_frame, values=server_names, state="readonly", # Запрещаем ручной ввод, можно только выбирать width=30 ) self.server_combo.pack(side='left', padx=5) # Если есть серверы, выбираем первый по умолчанию if server_names: self.server_combo.current(0) # Кнопка "Загрузить" для явного вызова self.btn_connect = tk.Button( self.server_frame, text="Загрузить скрипты", command=self.on_server_select ) self.btn_connect.pack(side='left', padx=5) self.server_combo.bind("<>") self.active_server_label = tk.Label(root, text="Выберите сервер", fg="blue") self.active_server_label.pack(pady=(0, 10)) # --- Основная часть (Скролл) --- self.main_frame = tk.Frame(root) self.main_frame.pack(expand=True, fill='both') self.label_title = tk.Label( self.main_frame, text="Выберите действие", font=("Arial", 14) ) self.label_title.pack(pady=10) self.canvas = tk.Canvas(self.main_frame) self.scrollbar = tk.Scrollbar(self.main_frame, orient="vertical", command=self.canvas.yview) self.scrollable_frame = tk.Frame(self.canvas) self.scrollable_frame.bind( "", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")) ) self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw") self.canvas.configure(yscrollcommand=self.scrollbar.set) self.canvas.pack(side="left", fill="both", expand=True, padx=20) self.scrollbar.pack(side="right", fill="y") self.status_label = tk.Label(root, text="Ожидание...", bd=1, relief=tk.SUNKEN, anchor=tk.W) self.status_label.pack(side=tk.BOTTOM, fill=tk.X) def on_server_select(self, event=None): """Срабатывает при выборе сервера из списка""" selected_name = self.server_combo.get() # Ищем данные сервера в общем списке по имени server_info = next((s for s in SERVERS if s['name'] == selected_name), None) if server_info: self.connect_to_server(server_info) else: self.status_label.config(text="Ошибка: сервер не найден") def connect_to_server(self, server_info): base_url = server_info['url'] name = server_info['name'] self.active_server_label.config(text=f"Активен: {name}") # Очистка for widget in self.scrollable_frame.winfo_children(): widget.destroy() self.status_label.config(text=f"Загрузка {MANIFEST_FILE}...") thread = threading.Thread(target=self.load_scripts_from_json, args=(base_url,), daemon=True) thread.start() def download_content(self, url): """Универсальный загрузчик (текст или бинарник)""" try: headers = {'User-Agent': 'Mozilla/5.0'} req = urllib.request.Request(url, headers=headers) with urllib.request.urlopen(req) as response: return response.read() except Exception as e: print(f"Ошибка загрузки {url}: {e}") return None def load_scripts_from_json(self, base_url): # 1. Формируем ссылку на JSON-файл manifest_url = urljoin(base_url, MANIFEST_FILE) json_data = self.download_content(manifest_url) if not json_data: self.root.after(0, lambda: self.status_label.config(text="Ошибка: не найден scripts.json")) return # 2. Парсим JSON try: script_list = json.loads(json_data) except json.JSONDecodeError: self.root.after(0, lambda: self.status_label.config(text="Ошибка: неверный формат JSON")) return # 3. Проходим по списку и скачиваем файлы count = 0 for item in script_list: name = item.get('name', 'Без имени') filename = item.get('file') if not filename: continue script_url = urljoin(base_url, filename) content = self.download_content(script_url) if content: # Определяем тип по расширению file_ext = filename.split('.')[-1].lower() # Создаем кнопку self.root.after(0, lambda n=name, c=content, f=filename, ext=file_ext: self.create_action_button(n, c, f, ext)) count += 1 if count == 0: self.root.after(0, lambda: self.status_label.config(text="Нет доступных скриптов")) else: self.root.after(0, lambda: self.status_label.config(text=f"Загружено: {count} скриптов")) def create_action_button(self, text, content, filename, ext): btn = tk.Button( self.scrollable_frame, text=text, width=40, height=2, anchor="w" ) btn.config(command=lambda c=content, f=filename, e=ext: self.run_file(c, f, e)) btn.pack(pady=2, fill='x') def run_file(self, content, filename, ext): try: # Создаем временный файл with tempfile.NamedTemporaryFile(mode='wb', suffix=f'.{ext}', delete=False) as tmp_file: tmp_file.write(content) tmp_path = tmp_file.name # Логика запуска для Linux if ext == 'bin': # 1. Делаем файл исполняемым (chmod +x) os.chmod(tmp_path, 0o755) # 2. Запускаем напрямую subprocess.Popen([tmp_path]) elif ext == 'sh': os.chmod(tmp_path, 0o755) subprocess.Popen(['xterm', '-e', tmp_path]) else: messagebox.showwarning("Внимание", f"Неизвестный тип файла: {ext}") # ИЗМЕНЕНИЕ: Убрали всплывающее окно, просто пишем в статус-бар self.status_label.config(text=f"Запущен: {filename}") except Exception as e: messagebox.showerror("Ошибка", f"Не удалось запустить: {e}") if __name__ == "__main__": root = tk.Tk() app = AdminHelper(root) root.mainloop()