feat: multi-type server support — SQL, Redis, Grafana, Prometheus, Telnet, WinRM, RDP/VNC
Full implementation of multi-type server management across GUI and CLI: New clients: SQLClient (MariaDB/MSSQL/PostgreSQL), RedisClient, GrafanaClient, PrometheusClient, TelnetSession, WinRMClient, RemoteDesktopLauncher. New GUI tabs: QueryTab (SQL editor + Treeview), RedisTab (console + history), GrafanaTab (dashboards + alerts), PrometheusTab (PromQL + targets), PowershellTab (PS/CMD), LaunchTab (RDP/VNC external client). Infrastructure: TAB_REGISTRY for conditional tabs per server type, adaptive server_dialog fields, colored type badges in sidebar, status checker for all types (SSH/TCP/SQL/Redis/HTTP), 100+ i18n keys. CLI: ssh.py extended with --sql, --redis, --grafana-*, --prom-*, --ps, --cmd. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Terminal tab — persistent interactive SSH shell via ShellSession + TerminalWidget.
|
||||
Terminal tab — persistent interactive SSH/Telnet shell via ShellSession/TelnetSession + TerminalWidget.
|
||||
"""
|
||||
|
||||
import queue
|
||||
@@ -8,6 +8,7 @@ import threading
|
||||
import time
|
||||
import customtkinter as ctk
|
||||
from core.ssh_client import ShellSession
|
||||
from core.telnet_client import TelnetSession
|
||||
from core.i18n import t
|
||||
|
||||
# Regex to strip ANSI escape sequences
|
||||
@@ -20,7 +21,7 @@ class TerminalTab(ctk.CTkFrame):
|
||||
self.store = store
|
||||
self.session_pool = session_pool
|
||||
self._current_alias: str | None = None
|
||||
self._session: ShellSession | None = None
|
||||
self._session: ShellSession | TelnetSession | None = None
|
||||
self._reconnect_count = 0
|
||||
self._max_reconnect = 5
|
||||
self._intentional_disconnect = False
|
||||
@@ -76,16 +77,25 @@ class TerminalTab(ctk.CTkFrame):
|
||||
return
|
||||
|
||||
alias = self._current_alias
|
||||
server_type = server.get("type", "ssh")
|
||||
self._terminal.set_status(t("term_connecting").format(alias=alias), "#ccaa00")
|
||||
self._intentional_disconnect = False
|
||||
|
||||
def _do_connect():
|
||||
try:
|
||||
key_path = self.store.get_ssh_key_path()
|
||||
cols, rows = self._terminal.get_size()
|
||||
|
||||
# Use session pool if available
|
||||
if self.session_pool:
|
||||
cols, rows = self._terminal.get_size()
|
||||
if server_type == "telnet":
|
||||
# Telnet — direct session, no pool (pool is SSH-specific)
|
||||
self.after(0, self._terminal.reset)
|
||||
session = TelnetSession(server, cols=cols, rows=rows)
|
||||
session.on_data = self._on_data_received
|
||||
session.on_disconnect = self._on_disconnected
|
||||
session.connect()
|
||||
self._session = session
|
||||
elif self.session_pool:
|
||||
# SSH with session pool
|
||||
session, is_new = self.session_pool.get_or_create_shell_session(alias, server, key_path)
|
||||
if is_new:
|
||||
# New session — reset terminal for clean start
|
||||
@@ -108,9 +118,8 @@ class TerminalTab(ctk.CTkFrame):
|
||||
session.on_disconnect = self._on_disconnected
|
||||
self._session = session
|
||||
else:
|
||||
# Legacy behavior without session pool
|
||||
# SSH without pool (legacy)
|
||||
self.after(0, self._terminal.reset)
|
||||
cols, rows = self._terminal.get_size()
|
||||
session = ShellSession(server, key_path, cols=cols, rows=rows)
|
||||
session.on_data = self._on_data_received
|
||||
session.on_disconnect = self._on_disconnected
|
||||
@@ -136,12 +145,18 @@ class TerminalTab(ctk.CTkFrame):
|
||||
|
||||
def _disconnect(self):
|
||||
self._intentional_disconnect = True
|
||||
# Only disconnect if we don't have a session pool (otherwise session stays alive)
|
||||
if not self.session_pool and self._session:
|
||||
if not self._session:
|
||||
return
|
||||
# Telnet sessions are never pooled — always disconnect directly
|
||||
if isinstance(self._session, TelnetSession):
|
||||
self._session.disconnect()
|
||||
self._session = None
|
||||
# If using session pool, session remains active in the pool
|
||||
elif self.session_pool and self._session:
|
||||
# SSH without session pool — disconnect directly
|
||||
elif not self.session_pool:
|
||||
self._session.disconnect()
|
||||
self._session = None
|
||||
# SSH with session pool — session remains active in the pool
|
||||
else:
|
||||
# Remove callbacks to prevent processing data after switch
|
||||
self._session.on_data = None
|
||||
self._session.on_disconnect = None
|
||||
|
||||
Reference in New Issue
Block a user