Files
elmprodvpn/selective-vpn-gui/controllers/routes_controller.py

162 lines
6.6 KiB
Python
Raw 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.
#!/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 --------