298 lines
12 KiB
Python
298 lines
12 KiB
Python
|
|
import os
|
||
|
|
from loguru import logger
|
||
|
|
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
|
||
|
|
QPushButton, QComboBox, QMessageBox, QTextEdit,
|
||
|
|
QGroupBox, QGridLayout, QProgressBar)
|
||
|
|
from PySide6.QtCore import Qt
|
||
|
|
|
||
|
|
from threads import (GunicornInstallThread, GunicornTestThread,
|
||
|
|
UploadGunicornServiceThread, ManageGunicornServiceThread)
|
||
|
|
|
||
|
|
|
||
|
|
class GunicornTab(QWidget):
|
||
|
|
def __init__(self, parent=None):
|
||
|
|
super().__init__(parent)
|
||
|
|
self.parent = parent
|
||
|
|
self.init_ui()
|
||
|
|
|
||
|
|
def init_ui(self):
|
||
|
|
layout = QVBoxLayout()
|
||
|
|
|
||
|
|
# Gunicorn配置组
|
||
|
|
config_group = QGroupBox("Gunicorn配置")
|
||
|
|
config_layout = QGridLayout()
|
||
|
|
|
||
|
|
config_layout.addWidget(QLabel("Django项目路径:"), 0, 0)
|
||
|
|
self.django_path_input = QLineEdit()
|
||
|
|
self.django_path_input.setPlaceholderText("/home/user/django_project")
|
||
|
|
config_layout.addWidget(self.django_path_input, 0, 1)
|
||
|
|
|
||
|
|
config_layout.addWidget(QLabel("服务名称:"), 1, 0)
|
||
|
|
self.service_name_input = QLineEdit("django_gunicorn")
|
||
|
|
config_layout.addWidget(self.service_name_input, 1, 1)
|
||
|
|
|
||
|
|
config_layout.addWidget(QLabel("端口:"), 2, 0)
|
||
|
|
self.port_input = QLineEdit("8000")
|
||
|
|
config_layout.addWidget(self.port_input, 2, 1)
|
||
|
|
|
||
|
|
config_layout.addWidget(QLabel("工作进程数:"), 3, 0)
|
||
|
|
self.workers_input = QLineEdit("3")
|
||
|
|
config_layout.addWidget(self.workers_input, 3, 1)
|
||
|
|
|
||
|
|
self.load_config_btn = QPushButton("加载配置")
|
||
|
|
self.load_config_btn.clicked.connect(self.load_gunicorn_config)
|
||
|
|
config_layout.addWidget(self.load_config_btn, 4, 1)
|
||
|
|
|
||
|
|
config_group.setLayout(config_layout)
|
||
|
|
layout.addWidget(config_group)
|
||
|
|
|
||
|
|
# Gunicorn操作组
|
||
|
|
gunicorn_group = QGroupBox("Gunicorn操作")
|
||
|
|
gunicorn_layout = QGridLayout()
|
||
|
|
|
||
|
|
self.install_gunicorn_btn = QPushButton("安装Gunicorn")
|
||
|
|
self.install_gunicorn_btn.clicked.connect(self.install_gunicorn)
|
||
|
|
gunicorn_layout.addWidget(self.install_gunicorn_btn, 0, 0)
|
||
|
|
|
||
|
|
self.test_gunicorn_btn = QPushButton("测试Gunicorn")
|
||
|
|
self.test_gunicorn_btn.clicked.connect(self.test_gunicorn)
|
||
|
|
gunicorn_layout.addWidget(self.test_gunicorn_btn, 0, 1)
|
||
|
|
|
||
|
|
self.upload_service_btn = QPushButton("上传服务文件")
|
||
|
|
self.upload_service_btn.clicked.connect(self.upload_service_file)
|
||
|
|
gunicorn_layout.addWidget(self.upload_service_btn, 1, 0)
|
||
|
|
|
||
|
|
self.start_service_btn = QPushButton("启动服务")
|
||
|
|
self.start_service_btn.clicked.connect(lambda: self.manage_service("start"))
|
||
|
|
gunicorn_layout.addWidget(self.start_service_btn, 1, 1)
|
||
|
|
|
||
|
|
self.stop_service_btn = QPushButton("停止服务")
|
||
|
|
self.stop_service_btn.clicked.connect(lambda: self.manage_service("stop"))
|
||
|
|
gunicorn_layout.addWidget(self.stop_service_btn, 2, 0)
|
||
|
|
|
||
|
|
self.restart_service_btn = QPushButton("重启服务")
|
||
|
|
self.restart_service_btn.clicked.connect(lambda: self.manage_service("restart"))
|
||
|
|
gunicorn_layout.addWidget(self.restart_service_btn, 2, 1)
|
||
|
|
|
||
|
|
self.status_service_btn = QPushButton("查看服务状态")
|
||
|
|
self.status_service_btn.clicked.connect(lambda: self.manage_service("status"))
|
||
|
|
gunicorn_layout.addWidget(self.status_service_btn, 3, 0)
|
||
|
|
|
||
|
|
self.enable_service_btn = QPushButton("启用开机自启")
|
||
|
|
self.enable_service_btn.clicked.connect(lambda: self.manage_service("enable"))
|
||
|
|
gunicorn_layout.addWidget(self.enable_service_btn, 3, 1)
|
||
|
|
|
||
|
|
gunicorn_group.setLayout(gunicorn_layout)
|
||
|
|
layout.addWidget(gunicorn_group)
|
||
|
|
|
||
|
|
# 服务文件编辑器
|
||
|
|
service_group = QGroupBox("Gunicorn服务文件")
|
||
|
|
service_layout = QVBoxLayout()
|
||
|
|
|
||
|
|
self.service_editor = QTextEdit()
|
||
|
|
self.service_editor.setPlaceholderText("Gunicorn服务文件内容将在这里显示...")
|
||
|
|
service_layout.addWidget(self.service_editor)
|
||
|
|
|
||
|
|
service_group.setLayout(service_layout)
|
||
|
|
layout.addWidget(service_group)
|
||
|
|
|
||
|
|
# 操作输出
|
||
|
|
output_group = QGroupBox("操作输出")
|
||
|
|
output_layout = QVBoxLayout()
|
||
|
|
|
||
|
|
self.output_text = QTextEdit()
|
||
|
|
self.output_text.setReadOnly(True)
|
||
|
|
self.output_text.setPlaceholderText("操作结果将在这里显示...")
|
||
|
|
output_layout.addWidget(self.output_text)
|
||
|
|
|
||
|
|
# 进度条
|
||
|
|
self.progress_bar = QProgressBar()
|
||
|
|
self.progress_bar.setVisible(False)
|
||
|
|
output_layout.addWidget(self.progress_bar)
|
||
|
|
|
||
|
|
output_group.setLayout(output_layout)
|
||
|
|
layout.addWidget(output_group)
|
||
|
|
|
||
|
|
layout.addStretch()
|
||
|
|
self.setLayout(layout)
|
||
|
|
|
||
|
|
# 加载Gunicorn配置
|
||
|
|
self.load_gunicorn_config()
|
||
|
|
|
||
|
|
def load_gunicorn_config(self):
|
||
|
|
if self.parent and hasattr(self.parent, 'server_connection_tab'):
|
||
|
|
django_path = self.parent.server_connection_tab.django_path_input.text()
|
||
|
|
self.django_path_input.setText(django_path)
|
||
|
|
|
||
|
|
# 生成默认的服务文件内容
|
||
|
|
service_name = self.service_name_input.text()
|
||
|
|
port = self.port_input.text()
|
||
|
|
workers = self.workers_input.text()
|
||
|
|
|
||
|
|
service_content = self.generate_service_file(service_name, django_path, port, workers)
|
||
|
|
self.service_editor.setText(service_content)
|
||
|
|
|
||
|
|
def generate_service_file(self, service_name, django_path, port, workers):
|
||
|
|
return f"""[Unit]
|
||
|
|
Description={service_name} daemon
|
||
|
|
After=network.target
|
||
|
|
|
||
|
|
[Service]
|
||
|
|
User=www-data
|
||
|
|
Group=www-data
|
||
|
|
WorkingDirectory={django_path}
|
||
|
|
ExecStart=/usr/local/bin/gunicorn --workers {workers} --bind 0.0.0.0:{port} {os.path.basename(django_path)}.wsgi:application
|
||
|
|
|
||
|
|
[Install]
|
||
|
|
WantedBy=multi-user.target
|
||
|
|
"""
|
||
|
|
|
||
|
|
def check_ssh_connection(self):
|
||
|
|
if not self.parent or not self.parent.ssh_client:
|
||
|
|
QMessageBox.warning(self, "警告", "请先连接服务器")
|
||
|
|
return False
|
||
|
|
return True
|
||
|
|
|
||
|
|
def install_gunicorn(self):
|
||
|
|
if not self.check_ssh_connection():
|
||
|
|
return
|
||
|
|
|
||
|
|
self.output_text.append("正在安装Gunicorn...")
|
||
|
|
self.install_gunicorn_btn.setEnabled(False)
|
||
|
|
self.progress_bar.setVisible(True)
|
||
|
|
self.progress_bar.setValue(0)
|
||
|
|
|
||
|
|
# 获取密码
|
||
|
|
password = None
|
||
|
|
if self.parent and hasattr(self.parent, 'server_connection_tab'):
|
||
|
|
password = self.parent.server_connection_tab.password_input.text()
|
||
|
|
|
||
|
|
self.gunicorn_install_thread = GunicornInstallThread(self.parent.ssh_client, password)
|
||
|
|
self.gunicorn_install_thread.progress_updated.connect(self.update_progress)
|
||
|
|
self.gunicorn_install_thread.result_ready.connect(self.on_install_gunicorn_result)
|
||
|
|
self.gunicorn_install_thread.start()
|
||
|
|
|
||
|
|
def update_progress(self, value):
|
||
|
|
self.progress_bar.setValue(value)
|
||
|
|
|
||
|
|
def on_install_gunicorn_result(self, success, message):
|
||
|
|
self.install_gunicorn_btn.setEnabled(True)
|
||
|
|
self.progress_bar.setVisible(False)
|
||
|
|
if success:
|
||
|
|
self.output_text.append(f"Gunicorn安装成功: {message}")
|
||
|
|
logger.info(f"Gunicorn安装成功: {message}")
|
||
|
|
else:
|
||
|
|
self.output_text.append(f"Gunicorn安装失败: {message}")
|
||
|
|
logger.error(f"Gunicorn安装失败: {message}")
|
||
|
|
|
||
|
|
def test_gunicorn(self):
|
||
|
|
if not self.check_ssh_connection():
|
||
|
|
return
|
||
|
|
|
||
|
|
django_path = self.django_path_input.text().strip()
|
||
|
|
if not django_path:
|
||
|
|
QMessageBox.warning(self, "警告", "请输入Django项目路径")
|
||
|
|
return
|
||
|
|
|
||
|
|
self.output_text.append(f"正在测试Gunicorn {django_path}...")
|
||
|
|
self.test_gunicorn_btn.setEnabled(False)
|
||
|
|
self.progress_bar.setVisible(True)
|
||
|
|
self.progress_bar.setValue(0)
|
||
|
|
|
||
|
|
self.gunicorn_test_thread = GunicornTestThread(self.parent.ssh_client, django_path)
|
||
|
|
self.gunicorn_test_thread.progress_updated.connect(self.update_progress)
|
||
|
|
self.gunicorn_test_thread.result_ready.connect(self.on_test_gunicorn_result)
|
||
|
|
self.gunicorn_test_thread.start()
|
||
|
|
|
||
|
|
def on_test_gunicorn_result(self, success, message):
|
||
|
|
self.test_gunicorn_btn.setEnabled(True)
|
||
|
|
self.progress_bar.setVisible(False)
|
||
|
|
if success:
|
||
|
|
self.output_text.append(f"Gunicorn测试成功: {message}")
|
||
|
|
logger.info(f"Gunicorn测试成功: {message}")
|
||
|
|
else:
|
||
|
|
self.output_text.append(f"Gunicorn测试失败: {message}")
|
||
|
|
logger.error(f"Gunicorn测试失败: {message}")
|
||
|
|
|
||
|
|
def upload_service_file(self):
|
||
|
|
if not self.check_ssh_connection():
|
||
|
|
return
|
||
|
|
|
||
|
|
service_name = self.service_name_input.text().strip()
|
||
|
|
service_content = self.service_editor.toPlainText()
|
||
|
|
|
||
|
|
if not service_name or not service_content:
|
||
|
|
QMessageBox.warning(self, "警告", "请输入服务名称并编辑服务文件内容")
|
||
|
|
return
|
||
|
|
|
||
|
|
self.output_text.append(f"正在上传服务文件 {service_name}...")
|
||
|
|
self.upload_service_btn.setEnabled(False)
|
||
|
|
|
||
|
|
# 获取密码
|
||
|
|
password = None
|
||
|
|
if self.parent and hasattr(self.parent, 'server_connection_tab'):
|
||
|
|
password = self.parent.server_connection_tab.password_input.text()
|
||
|
|
|
||
|
|
self.upload_thread = UploadGunicornServiceThread(self.parent.ssh_client, service_name, service_content, password)
|
||
|
|
self.upload_thread.result_ready.connect(self.on_upload_service_result)
|
||
|
|
self.upload_thread.start()
|
||
|
|
|
||
|
|
def on_upload_service_result(self, success, message):
|
||
|
|
self.upload_service_btn.setEnabled(True)
|
||
|
|
if success:
|
||
|
|
self.output_text.append(f"服务文件上传成功: {message}")
|
||
|
|
logger.info(f"服务文件上传成功: {message}")
|
||
|
|
else:
|
||
|
|
self.output_text.append(f"服务文件上传失败: {message}")
|
||
|
|
logger.error(f"服务文件上传失败: {message}")
|
||
|
|
|
||
|
|
def manage_service(self, action):
|
||
|
|
if not self.check_ssh_connection():
|
||
|
|
return
|
||
|
|
|
||
|
|
service_name = self.service_name_input.text().strip()
|
||
|
|
if not service_name:
|
||
|
|
QMessageBox.warning(self, "警告", "请输入服务名称")
|
||
|
|
return
|
||
|
|
|
||
|
|
self.output_text.append(f"正在执行服务 {action} 操作...")
|
||
|
|
|
||
|
|
# 禁用所有服务管理按钮
|
||
|
|
buttons = [self.start_service_btn, self.stop_service_btn, self.restart_service_btn,
|
||
|
|
self.status_service_btn, self.enable_service_btn]
|
||
|
|
for btn in buttons:
|
||
|
|
btn.setEnabled(False)
|
||
|
|
|
||
|
|
# 获取密码
|
||
|
|
password = None
|
||
|
|
if self.parent and hasattr(self.parent, 'server_connection_tab'):
|
||
|
|
password = self.parent.server_connection_tab.password_input.text()
|
||
|
|
|
||
|
|
self.manage_thread = ManageGunicornServiceThread(self.parent.ssh_client, service_name, action, password)
|
||
|
|
self.manage_thread.result_ready.connect(lambda s, m: self.on_manage_service_result(s, m, buttons))
|
||
|
|
self.manage_thread.start()
|
||
|
|
|
||
|
|
def on_manage_service_result(self, success, message, buttons):
|
||
|
|
# 重新启用所有服务管理按钮
|
||
|
|
for btn in buttons:
|
||
|
|
btn.setEnabled(True)
|
||
|
|
|
||
|
|
if success:
|
||
|
|
self.output_text.append(f"服务操作成功: {message}")
|
||
|
|
logger.info(f"服务操作成功: {message}")
|
||
|
|
else:
|
||
|
|
self.output_text.append(f"服务操作失败: {message}")
|
||
|
|
logger.error(f"服务操作失败: {message}")
|
||
|
|
|
||
|
|
def on_server_changed(self):
|
||
|
|
self.load_gunicorn_config()
|
||
|
|
|
||
|
|
def check_service_status(self):
|
||
|
|
if not self.check_ssh_connection():
|
||
|
|
return
|
||
|
|
|
||
|
|
service_name = self.service_name_input.text().strip()
|
||
|
|
if not service_name:
|
||
|
|
return
|
||
|
|
|
||
|
|
self.manage_service("status")
|