Files
xingrin/backend/apps/engine/services/worker_service.py

191 lines
5.6 KiB
Python
Raw Normal View History

2025-12-12 18:04:57 +08:00
"""
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)}"
2025-12-19 19:48:01 +08:00
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)
2025-12-12 18:04:57 +08:00
__all__ = ["WorkerService"]