a
This commit is contained in:
parent
38ebbebd95
commit
c92ce63a3a
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Optional, Tuple
|
from typing import Any, Dict, Optional, Tuple
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Header
|
from fastapi import APIRouter, HTTPException, Header
|
||||||
|
|
@ -28,6 +29,22 @@ def _build_supervisorctl_cmd(args: list[str]) -> list[str]:
|
||||||
supervisor_conf = os.getenv("SUPERVISOR_CONF", "").strip()
|
supervisor_conf = os.getenv("SUPERVISOR_CONF", "").strip()
|
||||||
use_sudo = os.getenv("SUPERVISOR_USE_SUDO", "false").lower() == "true"
|
use_sudo = os.getenv("SUPERVISOR_USE_SUDO", "false").lower() == "true"
|
||||||
|
|
||||||
|
# 如果没显式配置 SUPERVISOR_CONF,就尝试自动探测常见路径(宝塔/系统)
|
||||||
|
if not supervisor_conf:
|
||||||
|
candidates = [
|
||||||
|
"/www/server/panel/plugin/supervisor/supervisord.conf",
|
||||||
|
"/www/server/panel/plugin/supervisor/supervisor.conf",
|
||||||
|
"/etc/supervisor/supervisord.conf",
|
||||||
|
"/etc/supervisord.conf",
|
||||||
|
]
|
||||||
|
for p in candidates:
|
||||||
|
try:
|
||||||
|
if Path(p).exists():
|
||||||
|
supervisor_conf = p
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
cmd: list[str] = []
|
cmd: list[str] = []
|
||||||
if use_sudo:
|
if use_sudo:
|
||||||
# 需要你在 sudoers 配置 NOPASSWD(sudo -n 才不会卡住)
|
# 需要你在 sudoers 配置 NOPASSWD(sudo -n 才不会卡住)
|
||||||
|
|
@ -75,6 +92,89 @@ def _get_program_name() -> str:
|
||||||
return os.getenv("SUPERVISOR_TRADING_PROGRAM", "auto_sys").strip() or "auto_sys"
|
return os.getenv("SUPERVISOR_TRADING_PROGRAM", "auto_sys").strip() or "auto_sys"
|
||||||
|
|
||||||
|
|
||||||
|
def _select_best_process_name(program: str, status_all_raw: str) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
从 `supervisorctl status` 全量输出中,找到最匹配的真实进程名。
|
||||||
|
兼容 supervisor 的 group:process 格式,例如:auto_sys:auto_sys_00
|
||||||
|
"""
|
||||||
|
if not status_all_raw:
|
||||||
|
return None
|
||||||
|
|
||||||
|
lines = [ln.strip() for ln in status_all_raw.splitlines() if ln.strip()]
|
||||||
|
names: list[str] = []
|
||||||
|
for ln in lines:
|
||||||
|
name = ln.split(None, 1)[0].strip()
|
||||||
|
if name:
|
||||||
|
names.append(name)
|
||||||
|
|
||||||
|
# 精确优先:program / program_00 / program:program_00
|
||||||
|
preferred = [program, f"{program}_00", f"{program}:{program}_00"]
|
||||||
|
for cand in preferred:
|
||||||
|
if cand in names:
|
||||||
|
return cand
|
||||||
|
|
||||||
|
# 次优:任意以 program_ 开头
|
||||||
|
for name in names:
|
||||||
|
if name.startswith(program + "_"):
|
||||||
|
return name
|
||||||
|
|
||||||
|
# 次优:任意以 program: 开头
|
||||||
|
for name in names:
|
||||||
|
if name.startswith(program + ":"):
|
||||||
|
return name
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _status_with_fallback(program: str) -> Tuple[str, Optional[str], Optional[str]]:
|
||||||
|
"""
|
||||||
|
- 优先 `status <program>`
|
||||||
|
- 若 no such process:返回全量 status,并尝试解析真实 name(例如 auto_sys:auto_sys_00)
|
||||||
|
返回:(raw, resolved_name, status_all)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
raw = _run_supervisorctl(["status", program])
|
||||||
|
return raw, program, None
|
||||||
|
except Exception as e:
|
||||||
|
msg = str(e).lower()
|
||||||
|
if "no such process" not in msg:
|
||||||
|
raise
|
||||||
|
|
||||||
|
status_all = _run_supervisorctl(["status"])
|
||||||
|
resolved = _select_best_process_name(program, status_all)
|
||||||
|
if resolved:
|
||||||
|
try:
|
||||||
|
raw = _run_supervisorctl(["status", resolved])
|
||||||
|
return raw, resolved, status_all
|
||||||
|
except Exception:
|
||||||
|
# 兜底:至少把全量输出返回,方便你确认真实进程名
|
||||||
|
return status_all, None, status_all
|
||||||
|
return status_all, None, status_all
|
||||||
|
|
||||||
|
|
||||||
|
def _action_with_fallback(action: str, program: str) -> Tuple[str, Optional[str], Optional[str]]:
|
||||||
|
"""
|
||||||
|
对 start/stop/restart 做兜底:如果 program 不存在,尝试解析真实 name 再执行。
|
||||||
|
返回:(output, resolved_name, status_all)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
out = _run_supervisorctl([action, program])
|
||||||
|
return out, program, None
|
||||||
|
except Exception as e:
|
||||||
|
msg = str(e).lower()
|
||||||
|
if "no such process" not in msg:
|
||||||
|
raise
|
||||||
|
|
||||||
|
status_all = _run_supervisorctl(["status"])
|
||||||
|
resolved = _select_best_process_name(program, status_all)
|
||||||
|
if not resolved:
|
||||||
|
# 没找到就把全量输出带上,方便定位
|
||||||
|
raise RuntimeError(f"no such process: {program}. 当前 supervisor 进程列表:\n{status_all}")
|
||||||
|
|
||||||
|
out = _run_supervisorctl([action, resolved])
|
||||||
|
return out, resolved, status_all
|
||||||
|
|
||||||
|
|
||||||
@router.post("/clear-cache")
|
@router.post("/clear-cache")
|
||||||
async def clear_cache(x_admin_token: Optional[str] = Header(default=None, alias="X-Admin-Token")) -> Dict[str, Any]:
|
async def clear_cache(x_admin_token: Optional[str] = Header(default=None, alias="X-Admin-Token")) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
|
|
@ -147,15 +247,17 @@ async def trading_status(x_admin_token: Optional[str] = Header(default=None, ali
|
||||||
|
|
||||||
program = _get_program_name()
|
program = _get_program_name()
|
||||||
try:
|
try:
|
||||||
raw = _run_supervisorctl(["status", program])
|
raw, resolved_name, status_all = _status_with_fallback(program)
|
||||||
running, pid, state = _parse_supervisor_status(raw)
|
running, pid, state = _parse_supervisor_status(raw)
|
||||||
return {
|
return {
|
||||||
"mode": "supervisor",
|
"mode": "supervisor",
|
||||||
"program": program,
|
"program": program,
|
||||||
|
"resolved_name": resolved_name,
|
||||||
"running": running,
|
"running": running,
|
||||||
"pid": pid,
|
"pid": pid,
|
||||||
"state": state,
|
"state": state,
|
||||||
"raw": raw,
|
"raw": raw,
|
||||||
|
"status_all": status_all,
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|
@ -170,8 +272,8 @@ async def trading_start(x_admin_token: Optional[str] = Header(default=None, alia
|
||||||
|
|
||||||
program = _get_program_name()
|
program = _get_program_name()
|
||||||
try:
|
try:
|
||||||
out = _run_supervisorctl(["start", program])
|
out, resolved_name, status_all = _action_with_fallback("start", program)
|
||||||
raw = _run_supervisorctl(["status", program])
|
raw, resolved_name2, status_all2 = _status_with_fallback(resolved_name or program)
|
||||||
running, pid, state = _parse_supervisor_status(raw)
|
running, pid, state = _parse_supervisor_status(raw)
|
||||||
return {
|
return {
|
||||||
"message": "交易系统已启动(supervisor)",
|
"message": "交易系统已启动(supervisor)",
|
||||||
|
|
@ -179,10 +281,12 @@ async def trading_start(x_admin_token: Optional[str] = Header(default=None, alia
|
||||||
"status": {
|
"status": {
|
||||||
"mode": "supervisor",
|
"mode": "supervisor",
|
||||||
"program": program,
|
"program": program,
|
||||||
|
"resolved_name": resolved_name2 or resolved_name,
|
||||||
"running": running,
|
"running": running,
|
||||||
"pid": pid,
|
"pid": pid,
|
||||||
"state": state,
|
"state": state,
|
||||||
"raw": raw,
|
"raw": raw,
|
||||||
|
"status_all": status_all2 or status_all,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -195,8 +299,8 @@ async def trading_stop(x_admin_token: Optional[str] = Header(default=None, alias
|
||||||
|
|
||||||
program = _get_program_name()
|
program = _get_program_name()
|
||||||
try:
|
try:
|
||||||
out = _run_supervisorctl(["stop", program])
|
out, resolved_name, status_all = _action_with_fallback("stop", program)
|
||||||
raw = _run_supervisorctl(["status", program])
|
raw, resolved_name2, status_all2 = _status_with_fallback(resolved_name or program)
|
||||||
running, pid, state = _parse_supervisor_status(raw)
|
running, pid, state = _parse_supervisor_status(raw)
|
||||||
return {
|
return {
|
||||||
"message": "交易系统已停止(supervisor)",
|
"message": "交易系统已停止(supervisor)",
|
||||||
|
|
@ -204,10 +308,12 @@ async def trading_stop(x_admin_token: Optional[str] = Header(default=None, alias
|
||||||
"status": {
|
"status": {
|
||||||
"mode": "supervisor",
|
"mode": "supervisor",
|
||||||
"program": program,
|
"program": program,
|
||||||
|
"resolved_name": resolved_name2 or resolved_name,
|
||||||
"running": running,
|
"running": running,
|
||||||
"pid": pid,
|
"pid": pid,
|
||||||
"state": state,
|
"state": state,
|
||||||
"raw": raw,
|
"raw": raw,
|
||||||
|
"status_all": status_all2 or status_all,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -220,8 +326,8 @@ async def trading_restart(x_admin_token: Optional[str] = Header(default=None, al
|
||||||
|
|
||||||
program = _get_program_name()
|
program = _get_program_name()
|
||||||
try:
|
try:
|
||||||
out = _run_supervisorctl(["restart", program])
|
out, resolved_name, status_all = _action_with_fallback("restart", program)
|
||||||
raw = _run_supervisorctl(["status", program])
|
raw, resolved_name2, status_all2 = _status_with_fallback(resolved_name or program)
|
||||||
running, pid, state = _parse_supervisor_status(raw)
|
running, pid, state = _parse_supervisor_status(raw)
|
||||||
return {
|
return {
|
||||||
"message": "交易系统已重启(supervisor)",
|
"message": "交易系统已重启(supervisor)",
|
||||||
|
|
@ -229,10 +335,12 @@ async def trading_restart(x_admin_token: Optional[str] = Header(default=None, al
|
||||||
"status": {
|
"status": {
|
||||||
"mode": "supervisor",
|
"mode": "supervisor",
|
||||||
"program": program,
|
"program": program,
|
||||||
|
"resolved_name": resolved_name2 or resolved_name,
|
||||||
"running": running,
|
"running": running,
|
||||||
"pid": pid,
|
"pid": pid,
|
||||||
"state": state,
|
"state": state,
|
||||||
"raw": raw,
|
"raw": raw,
|
||||||
|
"status_all": status_all2 or status_all,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user