mirror of
https://github.com/yyhuni/xingrin.git
synced 2026-02-01 12:13:12 +08:00
191 lines
5.6 KiB
Python
191 lines
5.6 KiB
Python
"""
|
||
WorkerNode 业务逻辑服务层(Service)
|
||
|
||
负责 Worker 节点相关的业务逻辑处理
|
||
"""
|
||
|
||
import logging
|
||
from typing import Any
|
||
|
||
from apps.engine.repositories import DjangoWorkerRepository
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class WorkerService:
|
||
"""Worker 节点业务逻辑服务"""
|
||
|
||
def __init__(self) -> None:
|
||
"""初始化服务,注入 Repository 依赖"""
|
||
self.repo = DjangoWorkerRepository()
|
||
|
||
# ==================== 查询 ====================
|
||
|
||
def get_worker(self, worker_id: int):
|
||
"""根据 ID 获取 Worker 节点"""
|
||
return self.repo.get_by_id(worker_id)
|
||
|
||
def get_all_workers(self):
|
||
"""获取所有 Worker 节点查询集"""
|
||
return self.repo.get_all()
|
||
|
||
# ==================== 状态更新 ====================
|
||
|
||
def update_status(self, worker_id: int, status: str) -> bool:
|
||
"""更新 Worker 节点状态
|
||
|
||
Args:
|
||
worker_id: Worker ID
|
||
status: 状态 (pending/deploying/online/offline)
|
||
"""
|
||
return self.repo.update_status(worker_id, status)
|
||
|
||
|
||
def delete_worker(self, worker_id: int) -> bool:
|
||
"""删除 Worker 节点"""
|
||
return self.repo.delete_by_id(worker_id)
|
||
|
||
# ==================== 自注册 ====================
|
||
|
||
def register_worker(self, name: str, is_local: bool = True):
|
||
"""
|
||
注册 Worker 节点(本地 Worker 自注册用)
|
||
|
||
幂等操作:已存在则返回现有节点。
|
||
|
||
Args:
|
||
name: Worker 名称
|
||
is_local: 是否为本地节点
|
||
|
||
Returns:
|
||
(WorkerNode, created) 元组
|
||
"""
|
||
return self.repo.get_or_create_by_name(
|
||
name=name,
|
||
is_local=is_local
|
||
)
|
||
|
||
def remote_uninstall(
|
||
self,
|
||
worker_id: int,
|
||
ip_address: str,
|
||
ssh_port: int,
|
||
username: str,
|
||
password: str | None
|
||
) -> tuple[bool, str]:
|
||
"""
|
||
在远程主机上执行卸载脚本
|
||
|
||
Args:
|
||
worker_id: Worker ID(仅用于日志)
|
||
ip_address: SSH 主机地址
|
||
ssh_port: SSH 端口
|
||
username: SSH 用户名
|
||
password: SSH 密码
|
||
|
||
Returns:
|
||
(success, message) 元组
|
||
"""
|
||
if not password:
|
||
return False, "未配置 SSH 密码,跳过远程卸载"
|
||
|
||
try:
|
||
import paramiko
|
||
from apps.engine.services.deploy_service import get_uninstall_script
|
||
|
||
ssh = paramiko.SSHClient()
|
||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||
|
||
logger.info(f"[卸载] 正在连接 {ip_address}...")
|
||
ssh.connect(
|
||
ip_address,
|
||
port=ssh_port,
|
||
username=username,
|
||
password=password,
|
||
timeout=30
|
||
)
|
||
|
||
# 上传卸载脚本
|
||
uninstall_script = get_uninstall_script()
|
||
remote_script_path = '/tmp/xingrin_uninstall.sh'
|
||
|
||
sftp = ssh.open_sftp()
|
||
with sftp.file(remote_script_path, 'w') as f:
|
||
f.write(uninstall_script)
|
||
sftp.chmod(remote_script_path, 0o755)
|
||
sftp.close()
|
||
|
||
# 执行卸载脚本
|
||
logger.info(f"[卸载] 正在执行卸载脚本...")
|
||
stdin, stdout, stderr = ssh.exec_command(f"bash {remote_script_path}")
|
||
exit_status = stdout.channel.recv_exit_status()
|
||
|
||
ssh.close()
|
||
|
||
if exit_status == 0:
|
||
logger.info(f"[卸载] Worker {worker_id} 远程卸载成功")
|
||
return True, "远程卸载成功"
|
||
else:
|
||
error = stderr.read().decode().strip()
|
||
logger.warning(f"[卸载] Worker {worker_id} 远程卸载失败: {error}")
|
||
return False, f"远程卸载失败: {error}"
|
||
|
||
except Exception as e:
|
||
logger.warning(f"[卸载] Worker {worker_id} 远程卸载异常: {e}")
|
||
return False, f"远程卸载异常: {str(e)}"
|
||
|
||
def execute_remote_command(
|
||
self,
|
||
ip_address: str,
|
||
ssh_port: int,
|
||
username: str,
|
||
password: str | None,
|
||
command: str
|
||
) -> tuple[bool, str]:
|
||
"""
|
||
在远程主机上执行命令
|
||
|
||
Args:
|
||
ip_address: SSH 主机地址
|
||
ssh_port: SSH 端口
|
||
username: SSH 用户名
|
||
password: SSH 密码
|
||
command: 要执行的命令
|
||
|
||
Returns:
|
||
(success, message) 元组
|
||
"""
|
||
if not password:
|
||
return False, "未配置 SSH 密码"
|
||
|
||
try:
|
||
import paramiko
|
||
|
||
ssh = paramiko.SSHClient()
|
||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||
|
||
ssh.connect(
|
||
ip_address,
|
||
port=ssh_port,
|
||
username=username,
|
||
password=password,
|
||
timeout=30
|
||
)
|
||
|
||
stdin, stdout, stderr = ssh.exec_command(command, timeout=120)
|
||
exit_status = stdout.channel.recv_exit_status()
|
||
|
||
ssh.close()
|
||
|
||
if exit_status == 0:
|
||
return True, stdout.read().decode().strip()
|
||
else:
|
||
error = stderr.read().decode().strip()
|
||
return False, error
|
||
|
||
except Exception as e:
|
||
return False, str(e)
|
||
|
||
|
||
__all__ = ["WorkerService"]
|