commit e9c0e13341254f78275e9211ebdfdb69d7189514 Author: xiaji Date: Sun Aug 31 11:00:32 2025 +0800 初始化工程 diff --git a/__pycache__/remote_commands_tab.cpython-38.pyc b/__pycache__/remote_commands_tab.cpython-38.pyc new file mode 100644 index 0000000..8a0ac30 Binary files /dev/null and b/__pycache__/remote_commands_tab.cpython-38.pyc differ diff --git a/__pycache__/server_connection_tab.cpython-38.pyc b/__pycache__/server_connection_tab.cpython-38.pyc new file mode 100644 index 0000000..741e8c3 Binary files /dev/null and b/__pycache__/server_connection_tab.cpython-38.pyc differ diff --git a/app.log b/app.log new file mode 100644 index 0000000..2663fe2 --- /dev/null +++ b/app.log @@ -0,0 +1,66 @@ +2025-08-31 10:55:11.750 | INFO | __main__::44 - 启动应用程序 +2025-08-31 10:55:11.779 | INFO | __main__:__init__:13 - 初始化主窗口 +2025-08-31 10:55:11.781 | INFO | server_connection_tab:__init__:14 - 初始化服务器连接标签页 +2025-08-31 10:55:11.840 | INFO | server_connection_tab:init_ui:75 - 服务器连接标签页UI初始化完成 +2025-08-31 10:55:11.840 | INFO | server_connection_tab:load_config:78 - 加载配置文件 +2025-08-31 10:55:11.840 | INFO | server_connection_tab:load_config:87 - 配置文件不存在,将创建新文件: c:\Users\xiaji\Documents\个人文件夹\夏骥\桌面部署\config.json +2025-08-31 10:55:11.841 | INFO | remote_commands_tab:__init__:52 - 初始化远程命令标签页 +2025-08-31 10:55:11.842 | INFO | remote_commands_tab:init_ui:106 - 远程命令标签页UI初始化完成 +2025-08-31 10:55:11.843 | INFO | __main__:__init__:32 - 主窗口初始化完成 +2025-08-31 10:55:45.065 | INFO | __main__:on_tab_changed:35 - 切换到标签页: 1 +2025-08-31 10:55:45.065 | INFO | remote_commands_tab:set_ssh_client:109 - 设置SSH客户端 +2025-08-31 10:55:46.858 | INFO | __main__:on_tab_changed:35 - 切换到标签页: 0 +2025-08-31 10:56:00.654 | INFO | server_connection_tab:save_config:114 - 保存配置 +2025-08-31 10:56:31.724 | INFO | __main__::44 - 启动应用程序 +2025-08-31 10:56:31.750 | INFO | __main__:__init__:13 - 初始化主窗口 +2025-08-31 10:56:31.751 | INFO | server_connection_tab:__init__:14 - 初始化服务器连接标签页 +2025-08-31 10:56:31.757 | INFO | server_connection_tab:init_ui:87 - 服务器连接标签页UI初始化完成 +2025-08-31 10:56:31.758 | INFO | server_connection_tab:load_config:90 - 加载配置文件 +2025-08-31 10:56:31.758 | INFO | server_connection_tab:load_config:99 - 配置文件不存在,将创建新文件: c:\Users\xiaji\Documents\个人文件夹\夏骥\桌面部署\config.json +2025-08-31 10:56:31.759 | INFO | remote_commands_tab:__init__:52 - 初始化远程命令标签页 +2025-08-31 10:56:31.760 | INFO | remote_commands_tab:init_ui:106 - 远程命令标签页UI初始化完成 +2025-08-31 10:56:31.760 | INFO | __main__:__init__:32 - 主窗口初始化完成 +2025-08-31 10:56:42.212 | INFO | server_connection_tab:add_new_alias:126 - 添加新别名 +2025-08-31 10:56:42.214 | INFO | server_connection_tab:on_alias_changed:116 - 选择别名: 测试机 +2025-08-31 10:56:42.216 | INFO | server_connection_tab:add_new_alias:144 - 已添加新别名: 测试机 +2025-08-31 10:56:56.885 | INFO | server_connection_tab:save_config:147 - 保存配置 +2025-08-31 10:56:56.886 | INFO | server_connection_tab:save_config:171 - 配置已保存到: c:\Users\xiaji\Documents\个人文件夹\夏骥\桌面部署\config.json +2025-08-31 10:56:59.684 | INFO | server_connection_tab:connect_to_server:178 - 尝试连接服务器 +2025-08-31 10:56:59.855 | INFO | server_connection_tab:connect_to_server:197 - 成功连接到服务器: 192.168.3.157 +2025-08-31 10:57:02.873 | INFO | __main__:on_tab_changed:35 - 切换到标签页: 1 +2025-08-31 10:57:02.873 | INFO | remote_commands_tab:set_ssh_client:109 - 设置SSH客户端 +2025-08-31 10:57:03.659 | INFO | remote_commands_tab:install_git:120 - 安装Git +2025-08-31 10:57:03.664 | INFO | remote_commands_tab:run:18 - 执行远程命令: sudo apt update && sudo apt install -y git +2025-08-31 10:57:03.683 | ERROR | remote_commands_tab:run:40 - 命令执行失败,退出状态: 1 +2025-08-31 10:57:54.120 | INFO | __main__::44 - 启动应用程序 +2025-08-31 10:57:54.147 | INFO | __main__:__init__:13 - 初始化主窗口 +2025-08-31 10:57:54.148 | INFO | server_connection_tab:__init__:14 - 初始化服务器连接标签页 +2025-08-31 10:57:54.154 | INFO | server_connection_tab:init_ui:87 - 服务器连接标签页UI初始化完成 +2025-08-31 10:57:54.155 | INFO | server_connection_tab:load_config:90 - 加载配置文件 +2025-08-31 10:57:54.155 | INFO | server_connection_tab:load_config:97 - 成功加载配置文件: c:\Users\xiaji\Documents\个人文件夹\夏骥\桌面部署\config.json +2025-08-31 10:57:54.156 | INFO | server_connection_tab:on_alias_changed:116 - 选择别名: 测试机 +2025-08-31 10:57:54.157 | INFO | remote_commands_tab:__init__:116 - 初始化远程命令标签页 +2025-08-31 10:57:54.158 | INFO | remote_commands_tab:init_ui:170 - 远程命令标签页UI初始化完成 +2025-08-31 10:57:54.158 | INFO | __main__:__init__:32 - 主窗口初始化完成 +2025-08-31 10:57:58.773 | INFO | __main__::44 - 启动应用程序 +2025-08-31 10:57:58.792 | INFO | __main__:__init__:13 - 初始化主窗口 +2025-08-31 10:57:58.792 | INFO | server_connection_tab:__init__:14 - 初始化服务器连接标签页 +2025-08-31 10:57:58.799 | INFO | server_connection_tab:init_ui:87 - 服务器连接标签页UI初始化完成 +2025-08-31 10:57:58.799 | INFO | server_connection_tab:load_config:90 - 加载配置文件 +2025-08-31 10:57:58.800 | INFO | server_connection_tab:load_config:97 - 成功加载配置文件: c:\Users\xiaji\Documents\个人文件夹\夏骥\桌面部署\config.json +2025-08-31 10:57:58.800 | INFO | server_connection_tab:on_alias_changed:116 - 选择别名: 测试机 +2025-08-31 10:57:58.801 | INFO | remote_commands_tab:__init__:116 - 初始化远程命令标签页 +2025-08-31 10:57:58.802 | INFO | remote_commands_tab:init_ui:170 - 远程命令标签页UI初始化完成 +2025-08-31 10:57:58.802 | INFO | __main__:__init__:32 - 主窗口初始化完成 +2025-08-31 10:58:00.769 | INFO | server_connection_tab:connect_to_server:178 - 尝试连接服务器 +2025-08-31 10:58:00.859 | INFO | server_connection_tab:connect_to_server:197 - 成功连接到服务器: 192.168.3.157 +2025-08-31 10:58:02.607 | INFO | __main__:on_tab_changed:35 - 切换到标签页: 1 +2025-08-31 10:58:02.608 | INFO | remote_commands_tab:set_ssh_client:173 - 设置SSH客户端 +2025-08-31 10:58:03.712 | INFO | remote_commands_tab:install_git:184 - 安装Git +2025-08-31 10:58:03.714 | INFO | remote_commands_tab:run:53 - 执行远程命令: sudo apt update && sudo apt install -y git +2025-08-31 10:59:34.622 | INFO | remote_commands_tab:clone_repository:216 - 克隆仓库 +2025-08-31 10:59:34.624 | INFO | remote_commands_tab:run:53 - 执行远程命令: git clone http://192.168.3.241:3000/xiaji/django.remote.git +2025-08-31 10:59:34.684 | INFO | remote_commands_tab:run:101 - 命令执行成功: git clone http://192.168.3.241:3000/xiaji/django.remote.git +2025-08-31 11:00:00.984 | INFO | remote_commands_tab:clone_repository:216 - 克隆仓库 +2025-08-31 11:00:00.986 | INFO | remote_commands_tab:run:53 - 执行远程命令: git clone http://192.168.3.241:3000/xiaji/webstatus.git +2025-08-31 11:00:01.039 | INFO | remote_commands_tab:run:101 - 命令执行成功: git clone http://192.168.3.241:3000/xiaji/webstatus.git diff --git a/config.json b/config.json new file mode 100644 index 0000000..40607d9 --- /dev/null +++ b/config.json @@ -0,0 +1,9 @@ +{ + "测试机": { + "ip": "192.168.3.157", + "username": "xiaji", + "password": "xiaji", + "port": 22, + "project": "statuspage" + } +} \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..9d46503 --- /dev/null +++ b/main.py @@ -0,0 +1,49 @@ +import sys +from PySide6.QtWidgets import QApplication, QMainWindow, QTabWidget +from PySide6.QtCore import QSize +from loguru import logger + +from server_connection_tab import ServerConnectionTab +from remote_commands_tab import RemoteCommandsTab + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + + logger.info("初始化主窗口") + self.setWindowTitle("服务器管理工具") + self.setMinimumSize(QSize(800, 600)) + + # 创建标签页组件 + self.tabs = QTabWidget() + self.setCentralWidget(self.tabs) + + # 添加服务器连接标签页 + self.server_connection_tab = ServerConnectionTab() + self.tabs.addTab(self.server_connection_tab, "服务器连接") + + # 添加远程命令标签页 + self.remote_commands_tab = RemoteCommandsTab() + self.tabs.addTab(self.remote_commands_tab, "远程命令") + + # 连接标签页切换信号 + self.tabs.currentChanged.connect(self.on_tab_changed) + + logger.info("主窗口初始化完成") + + def on_tab_changed(self, index): + logger.info(f"切换到标签页: {index}") + + # 当切换到远程命令标签页时,传递SSH客户端 + if index == 1: # 远程命令标签页的索引 + ssh_client = self.server_connection_tab.get_ssh_client() + self.remote_commands_tab.set_ssh_client(ssh_client) + +if __name__ == "__main__": + logger.add("app.log", rotation="10 MB") + logger.info("启动应用程序") + + app = QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec()) \ No newline at end of file diff --git a/remote_commands_tab.py b/remote_commands_tab.py new file mode 100644 index 0000000..06bd5dc --- /dev/null +++ b/remote_commands_tab.py @@ -0,0 +1,247 @@ +from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, + QLineEdit, QPushButton, QGroupBox, QTextEdit, + QMessageBox, QFormLayout, QDialog, QDialogButtonBox) +from PySide6.QtCore import Qt, QThread, Signal +from loguru import logger + +class PasswordDialog(QDialog): + def __init__(self, parent=None): + super().__init__(parent) + self.setWindowTitle("输入密码") + self.setMinimumWidth(300) + + layout = QVBoxLayout() + + # 提示标签 + label = QLabel("请输入sudo密码:") + layout.addWidget(label) + + # 密码输入框 + self.password_input = QLineEdit() + self.password_input.setEchoMode(QLineEdit.Password) + layout.addWidget(self.password_input) + + # 按钮 + button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + button_box.accepted.connect(self.accept) + button_box.rejected.connect(self.reject) + layout.addWidget(button_box) + + self.setLayout(layout) + + def get_password(self): + return self.password_input.text() + +class RemoteCommandThread(QThread): + output_signal = Signal(str) + finished_signal = Signal(bool, str) + password_request_signal = Signal() # 请求密码的信号 + + def __init__(self, ssh_client, command): + super().__init__() + self.ssh_client = ssh_client + self.command = command + self.password = None + self.waiting_for_password = False + + def set_password(self, password): + self.password = password + self.waiting_for_password = False + + def run(self): + try: + logger.info(f"执行远程命令: {self.command}") + + # 如果命令包含sudo,修改为使用-S选项从标准输入读取密码 + if "sudo" in self.command: + command_with_sudo = self.command.replace("sudo", "sudo -S") + stdin, stdout, stderr = self.ssh_client.exec_command(command_with_sudo) + + # 检查是否需要密码 + password_prompt = False + for line in stderr: + self.output_signal.emit(line) + if "password for" in line.lower() or "密码" in line: + password_prompt = True + break + + # 如果需要密码,请求用户输入 + if password_prompt: + self.waiting_for_password = True + self.password_request_signal.emit() + + # 等待密码输入 + while self.waiting_for_password: + self.msleep(100) + + # 发送密码 + if self.password: + stdin.write(self.password + "\n") + stdin.flush() + else: + stdin, stdout, stderr = self.ssh_client.exec_command(self.command) + + # 读取输出 + output = "" + for line in stdout: + output += line + self.output_signal.emit(line) + + # 读取错误 + error = "" + for line in stderr: + error += line + if "password for" not in line.lower() and "密码" not in line: # 避免重复显示密码提示 + self.output_signal.emit(f"错误: {line}") + + # 检查退出状态 + exit_status = stdout.channel.recv_exit_status() + + if exit_status == 0: + logger.info(f"命令执行成功: {self.command}") + self.finished_signal.emit(True, "命令执行成功") + else: + logger.error(f"命令执行失败,退出状态: {exit_status}") + self.finished_signal.emit(False, f"命令执行失败,退出状态: {exit_status}") + + except Exception as e: + logger.error(f"执行命令时发生错误: {str(e)}") + self.output_signal.emit(f"错误: {str(e)}") + self.finished_signal.emit(False, f"执行命令时发生错误: {str(e)}") + +class RemoteCommandsTab(QWidget): + def __init__(self): + super().__init__() + + logger.info("初始化远程命令标签页") + self.ssh_client = None + self.command_thread = None + self.init_ui() + + def init_ui(self): + # 主布局 + main_layout = QVBoxLayout() + + # Git操作组 + git_group = QGroupBox("Git操作") + git_layout = QVBoxLayout() + + # 安装Git按钮 + install_git_layout = QHBoxLayout() + self.install_git_button = QPushButton("安装Git") + self.install_git_button.clicked.connect(self.install_git) + install_git_layout.addWidget(self.install_git_button) + install_git_layout.addStretch() + git_layout.addLayout(install_git_layout) + + # 克隆项目 + clone_layout = QFormLayout() + self.repo_url_input = QLineEdit() + clone_layout.addRow("仓库URL:", self.repo_url_input) + + self.clone_button = QPushButton("克隆项目") + self.clone_button.clicked.connect(self.clone_repository) + clone_layout.addRow(self.clone_button) + + git_layout.addLayout(clone_layout) + git_group.setLayout(git_layout) + main_layout.addWidget(git_group) + + # 命令输出区域 + output_group = QGroupBox("命令输出") + output_layout = QVBoxLayout() + + self.output_text = QTextEdit() + self.output_text.setReadOnly(True) + output_layout.addWidget(self.output_text) + + output_group.setLayout(output_layout) + main_layout.addWidget(output_group) + + # 状态栏 + self.status_label = QLabel("就绪") + self.status_label.setAlignment(Qt.AlignCenter) + main_layout.addWidget(self.status_label) + + # 添加伸缩空间 + main_layout.addStretch() + + self.setLayout(main_layout) + logger.info("远程命令标签页UI初始化完成") + + def set_ssh_client(self, ssh_client): + logger.info("设置SSH客户端") + self.ssh_client = ssh_client + + if self.ssh_client: + self.status_label.setText("已连接到服务器") + self.status_label.setStyleSheet("color: green;") + else: + self.status_label.setText("未连接到服务器") + self.status_label.setStyleSheet("color: red;") + + def install_git(self): + logger.info("安装Git") + + if not self.ssh_client: + QMessageBox.warning(self, "警告", "请先连接到服务器") + return + + self.output_text.clear() + self.status_label.setText("正在安装Git...") + + # 创建并启动线程执行命令 + self.command_thread = RemoteCommandThread(self.ssh_client, "sudo apt update && sudo apt install -y git") + self.command_thread.output_signal.connect(self.append_output) + self.command_thread.finished_signal.connect(self.on_command_finished) + self.command_thread.password_request_signal.connect(self.request_password) + self.command_thread.start() + + def request_password(self): + logger.info("请求输入sudo密码") + + # 创建密码输入对话框 + dialog = PasswordDialog(self) + if dialog.exec() == QDialog.Accepted: + password = dialog.get_password() + if password: + self.command_thread.set_password(password) + self.output_text.append("密码已发送\n") + else: + self.output_text.append("已取消输入密码\n") + self.command_thread.set_password("") + self.command_thread.waiting_for_password = False + + def clone_repository(self): + logger.info("克隆仓库") + + if not self.ssh_client: + QMessageBox.warning(self, "警告", "请先连接到服务器") + return + + repo_url = self.repo_url_input.text().strip() + if not repo_url: + QMessageBox.warning(self, "警告", "请输入仓库URL") + return + + self.output_text.clear() + self.status_label.setText("正在克隆仓库...") + + # 创建并启动线程执行命令 + self.command_thread = RemoteCommandThread(self.ssh_client, f"git clone {repo_url}") + self.command_thread.output_signal.connect(self.append_output) + self.command_thread.finished_signal.connect(self.on_command_finished) + self.command_thread.start() + + def append_output(self, text): + self.output_text.append(text) + + def on_command_finished(self, success, message): + if success: + self.status_label.setText(message) + self.status_label.setStyleSheet("color: green;") + QMessageBox.information(self, "成功", message) + else: + self.status_label.setText(message) + self.status_label.setStyleSheet("color: red;") + QMessageBox.warning(self, "错误", message) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6a812f6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +PySide6>=6.0.0 +paramiko>=2.7.0 +loguru>=0.5.0 \ No newline at end of file diff --git a/server_connection_tab.py b/server_connection_tab.py new file mode 100644 index 0000000..752df06 --- /dev/null +++ b/server_connection_tab.py @@ -0,0 +1,214 @@ +import json +import os +from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, + QLineEdit, QComboBox, QPushButton, QGroupBox, + QMessageBox, QFormLayout) +from PySide6.QtCore import Qt +import paramiko +from loguru import logger + +class ServerConnectionTab(QWidget): + def __init__(self): + super().__init__() + + logger.info("初始化服务器连接标签页") + self.ssh_client = None + self.config_data = {} + self.init_ui() + self.load_config() + + def init_ui(self): + # 主布局 + main_layout = QVBoxLayout() + + # 服务器配置组 + config_group = QGroupBox("服务器配置") + config_layout = QFormLayout() + + # 别名选择 + alias_layout = QHBoxLayout() + + self.alias_combo = QComboBox() + self.alias_combo.currentTextChanged.connect(self.on_alias_changed) + alias_layout.addWidget(self.alias_combo, 3) + + self.new_alias_input = QLineEdit() + self.new_alias_input.setPlaceholderText("输入新别名") + alias_layout.addWidget(self.new_alias_input, 2) + + self.add_alias_button = QPushButton("添加") + self.add_alias_button.clicked.connect(self.add_new_alias) + alias_layout.addWidget(self.add_alias_button, 1) + + config_layout.addRow("别名:", alias_layout) + + # 服务器信息输入 + self.ip_input = QLineEdit() + config_layout.addRow("IP地址:", self.ip_input) + + self.username_input = QLineEdit() + config_layout.addRow("用户名:", self.username_input) + + self.password_input = QLineEdit() + self.password_input.setEchoMode(QLineEdit.Password) + config_layout.addRow("密码:", self.password_input) + + self.port_input = QLineEdit("22") + config_layout.addRow("端口:", self.port_input) + + self.project_input = QLineEdit() + config_layout.addRow("项目名称:", self.project_input) + + config_group.setLayout(config_layout) + main_layout.addWidget(config_group) + + # 按钮区域 + button_layout = QHBoxLayout() + + self.save_button = QPushButton("保存配置") + self.save_button.clicked.connect(self.save_config) + button_layout.addWidget(self.save_button) + + self.connect_button = QPushButton("连接服务器") + self.connect_button.clicked.connect(self.connect_to_server) + button_layout.addWidget(self.connect_button) + + main_layout.addLayout(button_layout) + + # 连接状态 + self.status_label = QLabel("未连接") + self.status_label.setAlignment(Qt.AlignCenter) + main_layout.addWidget(self.status_label) + + # 添加伸缩空间 + main_layout.addStretch() + + self.setLayout(main_layout) + logger.info("服务器连接标签页UI初始化完成") + + def load_config(self): + logger.info("加载配置文件") + config_path = os.path.join(os.path.dirname(__file__), "config.json") + + try: + if os.path.exists(config_path): + with open(config_path, "r", encoding="utf-8") as f: + self.config_data = json.load(f) + logger.info(f"成功加载配置文件: {config_path}") + else: + logger.info(f"配置文件不存在,将创建新文件: {config_path}") + self.config_data = {} + + # 更新别名下拉框 + self.alias_combo.clear() + for alias in self.config_data.keys(): + self.alias_combo.addItem(alias) + + # 如果有配置,选择第一个别名 + if self.config_data: + self.alias_combo.setCurrentIndex(0) + + except Exception as e: + logger.error(f"加载配置文件失败: {str(e)}") + QMessageBox.warning(self, "错误", f"加载配置文件失败: {str(e)}") + + def on_alias_changed(self, alias): + logger.info(f"选择别名: {alias}") + if alias and alias in self.config_data: + server_info = self.config_data[alias] + self.ip_input.setText(server_info.get("ip", "")) + self.username_input.setText(server_info.get("username", "")) + self.password_input.setText(server_info.get("password", "")) + self.port_input.setText(str(server_info.get("port", "22"))) + self.project_input.setText(server_info.get("project", "")) + + def add_new_alias(self): + logger.info("添加新别名") + new_alias = self.new_alias_input.text().strip() + + if not new_alias: + QMessageBox.warning(self, "警告", "请输入新别名") + return + + if new_alias in self.config_data: + QMessageBox.warning(self, "警告", "该别名已存在") + return + + # 添加新别名到下拉框 + self.alias_combo.addItem(new_alias) + self.alias_combo.setCurrentText(new_alias) + + # 清空输入框 + self.new_alias_input.clear() + + logger.info(f"已添加新别名: {new_alias}") + + def save_config(self): + logger.info("保存配置") + alias = self.alias_combo.currentText() + + if not alias: + QMessageBox.warning(self, "警告", "请选择或添加别名") + return + + # 获取当前输入的值 + server_info = { + "ip": self.ip_input.text(), + "username": self.username_input.text(), + "password": self.password_input.text(), + "port": int(self.port_input.text()), + "project": self.project_input.text() + } + + # 更新配置数据 + self.config_data[alias] = server_info + + # 保存到文件 + config_path = os.path.join(os.path.dirname(__file__), "config.json") + try: + with open(config_path, "w", encoding="utf-8") as f: + json.dump(self.config_data, f, ensure_ascii=False, indent=4) + logger.info(f"配置已保存到: {config_path}") + QMessageBox.information(self, "成功", "配置已保存") + except Exception as e: + logger.error(f"保存配置失败: {str(e)}") + QMessageBox.warning(self, "错误", f"保存配置失败: {str(e)}") + + def connect_to_server(self): + logger.info("尝试连接服务器") + + # 获取连接信息 + ip = self.ip_input.text() + username = self.username_input.text() + password = self.password_input.text() + port = int(self.port_input.text()) + + if not all([ip, username, password]): + QMessageBox.warning(self, "警告", "请填写完整的连接信息") + return + + try: + # 创建SSH客户端 + self.ssh_client = paramiko.SSHClient() + self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + + # 连接服务器 + self.ssh_client.connect(ip, port=port, username=username, password=password) + logger.info(f"成功连接到服务器: {ip}") + + self.status_label.setText(f"已连接到 {ip}") + self.status_label.setStyleSheet("color: green;") + QMessageBox.information(self, "成功", f"成功连接到服务器: {ip}") + + except Exception as e: + logger.error(f"连接服务器失败: {str(e)}") + self.status_label.setText("连接失败") + self.status_label.setStyleSheet("color: red;") + QMessageBox.warning(self, "错误", f"连接服务器失败: {str(e)}") + + if self.ssh_client: + self.ssh_client.close() + self.ssh_client = None + + def get_ssh_client(self): + return self.ssh_client \ No newline at end of file