zapret-kvn/xray_fluent/engines/xray/balancer_api.py
loop-uh d5a4715771
Some checks failed
Windows project source guards / test (push) Has been cancelled
feat: use official Amnezia transport and organize runtime modules
2026-09-06 00:14:08 +03:00

86 lines
3.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Управление балансировщиком работающего xray через его gRPC API.
``xray api bo`` фиксирует выбор балансировщика на конкретном outbound'е. Ядро при
этом не перезапускается, конфиг на диске не переписывается, уже установленные
соединения не рвутся — новые сразу идут через выбранный сервер. Это и есть механизм
переключения серверов при активной ротации.
Команда требует ``"RoutingService"`` в секции ``api.services`` конфига; его добавляет
:func:`xray_fluent.application.outbound_pool_service.ensure_xray_pool_control_plane`
вместе с самим балансировщиком, когда ноды загружаются в ядро пулом.
"""
from __future__ import annotations
from pathlib import Path
from ...constants import PROXY_HOST
from ...platform.windows.subprocess_utils import CREATE_NO_WINDOW, result_output_text, run_text, run_text_pumped
#: Команда короткая и локальная; ждать дольше смысла нет.
COMMAND_TIMEOUT_SEC = 5.0
def build_balancer_override_command(
xray_path: str | Path,
api_port: int,
balancer_tag: str,
outbound_tag: str = "",
*,
remove: bool = False,
) -> list[str]:
"""Собрать argv для ``xray api bo``. Без ``outbound_tag`` требуется ``remove=True``."""
if not str(xray_path or "").strip():
raise ValueError("Путь к xray не задан")
if int(api_port) <= 0:
raise ValueError("Порт API xray не задан")
if not str(balancer_tag or "").strip():
raise ValueError("Тег балансировщика не задан")
command = [
str(xray_path),
"api",
"bo",
f"--server={PROXY_HOST}:{int(api_port)}",
"-b",
str(balancer_tag),
]
if remove:
command.append("-r")
return command
if not str(outbound_tag or "").strip():
raise ValueError("Тег outbound не задан")
command.append(str(outbound_tag))
return command
def apply_balancer_override(
xray_path: str | Path,
api_port: int,
balancer_tag: str,
outbound_tag: str = "",
*,
remove: bool = False,
pump: bool = True,
) -> tuple[bool, str]:
"""Применить (или снять) фиксацию выбора. Возвращает ``(успех, текст вывода)``.
``pump=True`` прокачивает события Qt на время ожидания: вызов идёт из главного
потока, и без этого зависший процесс подморозил бы интерфейс.
"""
try:
command = build_balancer_override_command(
xray_path, api_port, balancer_tag, outbound_tag, remove=remove
)
except ValueError as exc:
return False, str(exc)
runner = run_text_pumped if pump else run_text
try:
result = runner(command, timeout=COMMAND_TIMEOUT_SEC, creationflags=CREATE_NO_WINDOW)
except Exception as exc:
return False, f"{type(exc).__name__}: {exc}"
return result.returncode == 0, result_output_text(result).strip()