dev
This commit is contained in:
188
domain-api/app/services/worker_control_service.py
Normal file
188
domain-api/app/services/worker_control_service.py
Normal file
@@ -0,0 +1,188 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from app.core.config import settings
|
||||
from app.services.runtime_settings_service import get_runtime_settings
|
||||
|
||||
|
||||
def _domain_root() -> Path:
|
||||
return Path(settings.domain_root)
|
||||
|
||||
|
||||
def _runtime_config() -> dict:
|
||||
return get_runtime_settings()
|
||||
|
||||
|
||||
def _run_powershell(command: str, timeout: int = 20) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
|
||||
def _run_shell(command: list[str], timeout: int = 20) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(command, capture_output=True, text=True, timeout=timeout)
|
||||
|
||||
|
||||
def _windows_runtime() -> dict:
|
||||
command = """
|
||||
$targets = Get-CimInstance Win32_Process -Filter "name='python.exe'" |
|
||||
Where-Object { $_.CommandLine -like '*detect_worker.py*' } |
|
||||
Select-Object ProcessId, CommandLine
|
||||
if (-not $targets) {
|
||||
Write-Output '{"running":false,"process_count":0,"latest_start_time":"","mode":"windows-local"}'
|
||||
exit 0
|
||||
}
|
||||
$latest = $null
|
||||
foreach ($item in $targets) {
|
||||
try {
|
||||
$proc = Get-Process -Id $item.ProcessId -ErrorAction Stop
|
||||
if (-not $latest -or $proc.StartTime -gt $latest.StartTime) {
|
||||
$latest = $proc
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
$payload = @{
|
||||
running = $true
|
||||
process_count = @($targets).Count
|
||||
latest_start_time = if ($latest) { $latest.StartTime.ToString('yyyy-MM-dd HH:mm:ss') } else { '' }
|
||||
mode = 'windows-local'
|
||||
} | ConvertTo-Json -Compress
|
||||
Write-Output $payload
|
||||
"""
|
||||
result = _run_powershell(command)
|
||||
output = (result.stdout or "").strip()
|
||||
if result.returncode != 0 or not output:
|
||||
return {
|
||||
"mode": "windows-local",
|
||||
"running": False,
|
||||
"process_count": 0,
|
||||
"latest_start_time": "",
|
||||
"message": (result.stderr or result.stdout or "worker runtime probe failed").strip(),
|
||||
}
|
||||
try:
|
||||
payload = json.loads(output)
|
||||
except json.JSONDecodeError:
|
||||
return {
|
||||
"mode": "windows-local",
|
||||
"running": False,
|
||||
"process_count": 0,
|
||||
"latest_start_time": "",
|
||||
"message": output,
|
||||
}
|
||||
payload.setdefault("message", "")
|
||||
return payload
|
||||
|
||||
|
||||
def _linux_runtime() -> dict:
|
||||
runtime = _runtime_config()
|
||||
service_name = runtime["worker_service_name"]
|
||||
result = _run_shell(["systemctl", "show", service_name, "--no-page", "--property=ActiveState,SubState,MainPID"])
|
||||
output = (result.stdout or result.stderr or "").strip()
|
||||
if result.returncode != 0:
|
||||
return {
|
||||
"mode": "linux-systemd",
|
||||
"running": False,
|
||||
"process_count": 0,
|
||||
"latest_start_time": "",
|
||||
"message": output or f"systemd service {service_name} not available",
|
||||
}
|
||||
|
||||
data: dict[str, str] = {}
|
||||
for line in output.splitlines():
|
||||
if "=" in line:
|
||||
key, value = line.split("=", 1)
|
||||
data[key] = value
|
||||
main_pid = int(data.get("MainPID", "0") or 0)
|
||||
active_state = data.get("ActiveState", "")
|
||||
sub_state = data.get("SubState", "")
|
||||
return {
|
||||
"mode": "linux-systemd",
|
||||
"running": active_state == "active",
|
||||
"process_count": 1 if main_pid > 0 else 0,
|
||||
"latest_start_time": "",
|
||||
"message": f"{active_state}/{sub_state}" if active_state else "",
|
||||
}
|
||||
|
||||
|
||||
def detect_worker_runtime() -> dict:
|
||||
runtime = _runtime_config()
|
||||
worker_mode = runtime["worker_mode"]
|
||||
if worker_mode == "linux-systemd":
|
||||
return _linux_runtime()
|
||||
if os.name == "nt":
|
||||
return _windows_runtime()
|
||||
return {
|
||||
"mode": worker_mode,
|
||||
"running": False,
|
||||
"process_count": 0,
|
||||
"latest_start_time": "",
|
||||
"message": f"unsupported worker_mode: {worker_mode}",
|
||||
}
|
||||
|
||||
|
||||
def start_worker() -> tuple[bool, str]:
|
||||
runtime = _runtime_config()
|
||||
worker_mode = runtime["worker_mode"]
|
||||
service_name = runtime["worker_service_name"]
|
||||
if worker_mode == "linux-systemd":
|
||||
result = _run_shell(["systemctl", "start", service_name], timeout=30)
|
||||
if result.returncode != 0:
|
||||
return False, (result.stderr or result.stdout or "启动 Linux Worker 失败").strip()
|
||||
return True, f"Linux Worker 启动命令已发送: {service_name}"
|
||||
|
||||
if os.name != "nt":
|
||||
return False, "当前仅实现 Windows 本地 Worker 启动,Linux 请将 worker_mode 设为 linux-systemd。"
|
||||
|
||||
script_path = _domain_root() / "start_worker.ps1"
|
||||
if not script_path.exists():
|
||||
return False, f"未找到启动脚本: {script_path}"
|
||||
|
||||
command = (
|
||||
"Start-Process powershell "
|
||||
f"-ArgumentList '-ExecutionPolicy Bypass -File \"{script_path}\"' "
|
||||
f"-WorkingDirectory '{_domain_root()}'"
|
||||
)
|
||||
result = _run_powershell(command)
|
||||
if result.returncode != 0:
|
||||
return False, (result.stderr or result.stdout or "启动检测端失败").strip()
|
||||
return True, "检测端启动命令已发送"
|
||||
|
||||
|
||||
def stop_worker() -> tuple[bool, str]:
|
||||
runtime = _runtime_config()
|
||||
worker_mode = runtime["worker_mode"]
|
||||
service_name = runtime["worker_service_name"]
|
||||
if worker_mode == "linux-systemd":
|
||||
result = _run_shell(["systemctl", "stop", service_name], timeout=30)
|
||||
if result.returncode != 0:
|
||||
return False, (result.stderr or result.stdout or "停止 Linux Worker 失败").strip()
|
||||
return True, f"Linux Worker 停止命令已发送: {service_name}"
|
||||
|
||||
if os.name != "nt":
|
||||
return False, "当前仅实现 Windows 本地 Worker 停止,Linux 请将 worker_mode 设为 linux-systemd。"
|
||||
|
||||
command = """
|
||||
$targets = Get-CimInstance Win32_Process -Filter "name='python.exe'" |
|
||||
Where-Object { $_.CommandLine -like '*detect_worker.py*' } |
|
||||
Select-Object -ExpandProperty ProcessId
|
||||
if (-not $targets) {
|
||||
Write-Output 'NO_PROCESS'
|
||||
exit 0
|
||||
}
|
||||
$targets | ForEach-Object { Stop-Process -Id $_ -Force }
|
||||
Write-Output ('STOPPED:' + (($targets | Measure-Object).Count))
|
||||
"""
|
||||
result = _run_powershell(command)
|
||||
output = (result.stdout or result.stderr or "").strip()
|
||||
if result.returncode != 0:
|
||||
return False, output or "停止检测端失败"
|
||||
if "NO_PROCESS" in output:
|
||||
return True, "当前没有运行中的检测端进程"
|
||||
return True, output or "检测端已停止"
|
||||
Reference in New Issue
Block a user