"""Агент-режим: установка/удаление агента, приём метрик.""" import flet as ft from app.core import agent as ag from app.core import servers as servers_core from app.ui.components import confirm_dialog, snack class AgentPage(ft.Column): def __init__(self, page: ft.Page, go): super().__init__(expand=True, scroll=ft.ScrollMode.AUTO, spacing=12) self._page = page self.cards_col = ft.Column(spacing=10) self.server_status = ft.Text("…", size=13) self.controls = [ ft.Text("Агент-режим", size=28, weight=ft.FontWeight.BOLD), ft.Text("Агент на сервере шлёт метрики каждые 5 сек по HTTP — " "real-time графики без SSH-поллинга. Приложение слушает порт " f"{ag.get_agent_port()}.", size=13, color=ft.Colors.GREY_500), ft.Row([ ft.FilledButton("Запустить приёмник", icon=ft.Icons.PLAY_ARROW, on_click=self._start_server), self.server_status, ]), ft.Divider(), self.cards_col, ] self._reload() self._update_server_status() def _update_server_status(self): self.server_status.value = ( f"✅ приёмник запущен на порту {ag.get_agent_port()}" if ag.is_running() else "⚪ приёмник не запущен" ) self._page.update() def _start_server(self, e): async def _s(): await ag.start() self._update_server_status() self._page.run_task(_s) def _reload(self): self.cards_col.controls = [] for srv in servers_core.list_servers(): self.cards_col.controls.append(self._card(srv)) self._page.update() for srv, card in zip(servers_core.list_servers(), self.cards_col.controls): self._page.run_task(self._fill, srv, card) def _card(self, srv): return ft.Card(content=ft.Container( padding=14, width=560, content=ft.Column([ ft.Text(srv.name, weight=ft.FontWeight.BOLD, size=16), ft.ProgressBar(height=3), ]), )) async def _fill(self, srv, card): status = await ag.agent_status(srv) metrics = ag.agent_metrics.get(srv.id) running = status == "active" rows = [ft.Text(f"статус: {status}", size=12, color=ft.Colors.GREEN_300 if running else ft.Colors.GREY_500)] if metrics: import time age = time.time() - metrics.get("ts", 0) rows.append(ft.Text( f"CPU {metrics.get('cpu')}% • RAM {metrics.get('mem_pct')}% • " f"Диск {metrics.get('disk')}% • load {metrics.get('load')} • " f"обновлено {age:.0f} сек назад", size=11, color=ft.Colors.BLUE_300)) btns = [] if not running: btns.append(ft.FilledButton("Установить агента", icon=ft.Icons.DOWNLOAD, on_click=lambda e, s=srv: self._install(s))) else: btns.append(ft.OutlinedButton("Удалить агента", on_click=lambda e, s=srv: self._uninstall(s))) rows.append(ft.Row(btns)) card.content.content.controls[1] = ft.Column(rows, spacing=4) self._page.update() def _install(self, srv): from app.core.tasks import tasks import socket app_host = socket.gethostbyname(socket.gethostname()) async def job(log): log(f"адрес приложения для агента: {app_host}:{ag.get_agent_port()}") ok = await ag.install_agent(srv, app_host, log) log("готово" if ok else "ошибка установки") tasks.start("agent", "установка агента", srv.id, job, on_done=lambda r: self._reload()) snack(self._page, f"Установка агента на {srv.name}…") def _uninstall(self, srv): def do(): async def _d(): await ag.uninstall_agent(srv) self._reload() self._page.run_task(_d) confirm_dialog(self._page, "Удалить агента?", f"dm-agent будет остановлен и удалён с «{srv.name}».", do)