162 lines
6.6 KiB
Python
162 lines
6.6 KiB
Python
#!/usr/bin/env python3
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
from typing import cast
|
||
|
||
from api_client import CmdResult, Event
|
||
|
||
from .views import ActionView, RoutesNftProgressView, RoutesResolveSummaryView, ServiceAction
|
||
|
||
|
||
class RoutesControllerMixin:
|
||
def routes_service_action(self, action: str) -> ActionView:
|
||
act = action.strip().lower()
|
||
if act not in ("start", "stop", "restart"):
|
||
raise ValueError(f"Invalid routes action: {action}")
|
||
res = self.client.routes_service(cast(ServiceAction, act))
|
||
return ActionView(ok=res.ok, pretty_text=self._pretty_cmd(res))
|
||
|
||
def routes_clear(self) -> ActionView:
|
||
res = self.client.routes_clear()
|
||
return ActionView(ok=res.ok, pretty_text=self._pretty_cmd(res))
|
||
|
||
def routes_cache_restore(self) -> ActionView:
|
||
res = self.client.routes_cache_restore()
|
||
return ActionView(ok=res.ok, pretty_text=self._pretty_cmd(res))
|
||
|
||
def routes_precheck_debug(self, run_now: bool = True) -> ActionView:
|
||
res = self.client.routes_precheck_debug(run_now=run_now)
|
||
return ActionView(ok=res.ok, pretty_text=self._pretty_cmd(res))
|
||
|
||
def routes_fix_policy_route(self) -> ActionView:
|
||
res = self.client.routes_fix_policy_route()
|
||
return ActionView(ok=res.ok, pretty_text=self._pretty_cmd(res))
|
||
|
||
def routes_timer_enabled(self) -> bool:
|
||
st = self.client.routes_timer_get()
|
||
return bool(st.enabled)
|
||
|
||
def routes_timer_set(self, enabled: bool) -> ActionView:
|
||
res = self.client.routes_timer_set(bool(enabled))
|
||
return ActionView(ok=res.ok, pretty_text=self._pretty_cmd(res))
|
||
|
||
def routes_resolve_summary_view(self) -> RoutesResolveSummaryView:
|
||
dump = self.client.trace_get("full")
|
||
lines = list(getattr(dump, "lines", []) or [])
|
||
line = ""
|
||
for raw in reversed(lines):
|
||
s = str(raw or "")
|
||
if "resolve summary:" in s:
|
||
line = s
|
||
break
|
||
if not line:
|
||
return RoutesResolveSummaryView(
|
||
available=False,
|
||
text="Resolve summary: no data yet",
|
||
recheck_text="Timeout recheck: —",
|
||
color="gray",
|
||
recheck_color="gray",
|
||
)
|
||
|
||
tail = line.split("resolve summary:", 1)[1]
|
||
pairs: dict[str, int] = {}
|
||
for m in re.finditer(r"([a-zA-Z0-9_]+)=(-?\d+)", tail):
|
||
k = str(m.group(1) or "").strip().lower()
|
||
try:
|
||
pairs[k] = int(m.group(2))
|
||
except Exception:
|
||
continue
|
||
|
||
unique_ips = int(pairs.get("unique_ips", 0))
|
||
direct_ips = int(pairs.get("direct_ips", 0))
|
||
wildcard_ips = int(pairs.get("wildcard_ips", 0))
|
||
unresolved = int(pairs.get("unresolved", 0))
|
||
unresolved_live = int(pairs.get("unresolved_live", 0))
|
||
unresolved_suppressed = int(pairs.get("unresolved_suppressed", 0))
|
||
q_hits = int(pairs.get("quarantine_hits", 0))
|
||
dns_attempts = int(pairs.get("dns_attempts", 0))
|
||
dns_timeout = int(pairs.get("dns_timeout", 0))
|
||
dns_cooldown_skips = int(pairs.get("dns_cooldown_skips", 0))
|
||
live_batch_target = int(pairs.get("live_batch_target", 0))
|
||
live_batch_deferred = int(pairs.get("live_batch_deferred", 0))
|
||
live_batch_p1 = int(pairs.get("live_batch_p1", 0))
|
||
live_batch_p2 = int(pairs.get("live_batch_p2", 0))
|
||
live_batch_p3 = int(pairs.get("live_batch_p3", 0))
|
||
live_batch_nxheavy_pct = int(pairs.get("live_batch_nxheavy_pct", 0))
|
||
live_batch_nxheavy_skip = int(pairs.get("live_batch_nxheavy_skip", 0))
|
||
|
||
r_checked = int(pairs.get("timeout_recheck_checked", 0))
|
||
r_recovered = int(pairs.get("timeout_recheck_recovered", 0))
|
||
r_recovered_ips = int(pairs.get("timeout_recheck_recovered_ips", 0))
|
||
r_still_timeout = int(pairs.get("timeout_recheck_still_timeout", 0))
|
||
r_now_nx = int(pairs.get("timeout_recheck_now_nxdomain", 0))
|
||
r_now_tmp = int(pairs.get("timeout_recheck_now_temporary", 0))
|
||
|
||
text = (
|
||
f"Resolve: ips={unique_ips} (direct={direct_ips}, wildcard={wildcard_ips}, "
|
||
f"+recheck_ips={r_recovered_ips}) | unresolved={unresolved} "
|
||
f"(live={unresolved_live}, suppressed={unresolved_suppressed}) | "
|
||
f"quarantine_hits={q_hits} | dns_timeout={dns_timeout} "
|
||
f"| cooldown_skips={dns_cooldown_skips} | attempts={dns_attempts} "
|
||
f"| live_batch={live_batch_target} deferred={live_batch_deferred} "
|
||
f"(p1={live_batch_p1}, p2={live_batch_p2}, p3={live_batch_p3}, nx_pct={live_batch_nxheavy_pct}, nx_skip={live_batch_nxheavy_skip})"
|
||
)
|
||
recheck_text = (
|
||
f"Timeout recheck: checked={r_checked} recovered={r_recovered} "
|
||
f"still_timeout={r_still_timeout} now_nxdomain={r_now_nx} now_temporary={r_now_tmp}"
|
||
)
|
||
|
||
color = "green" if unresolved < 4000 else ("#b58900" if unresolved < 10000 else "red")
|
||
if dns_timeout > 500 and color == "green":
|
||
color = "#b58900"
|
||
if live_batch_p3 > 0 and (live_batch_p1+live_batch_p2) > 0:
|
||
ratio = float(live_batch_p3) / float(live_batch_p1 + live_batch_p2 + live_batch_p3)
|
||
if ratio > 0.8:
|
||
color = "#b58900" if color == "green" else color
|
||
if ratio > 0.95:
|
||
color = "red"
|
||
recheck_color = "green" if r_still_timeout <= 20 else ("#b58900" if r_still_timeout <= 100 else "red")
|
||
return RoutesResolveSummaryView(
|
||
available=True,
|
||
text=text,
|
||
recheck_text=recheck_text,
|
||
color=color,
|
||
recheck_color=recheck_color,
|
||
)
|
||
|
||
|
||
def routes_nft_progress_from_event(self, ev: Event) -> RoutesNftProgressView:
|
||
"""
|
||
Превращает Event(kind='routes_nft_progress') в удобную модель
|
||
для прогресс-бара/лейбла.
|
||
"""
|
||
payload = (
|
||
getattr(ev, "data", None)
|
||
or getattr(ev, "payload", None)
|
||
or getattr(ev, "extra", None)
|
||
or {}
|
||
)
|
||
|
||
if not isinstance(payload, dict):
|
||
payload = {}
|
||
|
||
try:
|
||
percent = int(payload.get("percent", 0))
|
||
except Exception:
|
||
percent = 0
|
||
|
||
msg = str(payload.get("message", "")) if payload is not None else ""
|
||
if not msg:
|
||
msg = "Updating nft set…"
|
||
|
||
active = 0 <= percent < 100
|
||
|
||
return RoutesNftProgressView(
|
||
percent=percent,
|
||
message=msg,
|
||
active=active,
|
||
)
|
||
|
||
# -------- DNS / SmartDNS --------
|