gui(routes): show resolve summary + timeout recheck badges
This commit is contained in:
@@ -156,6 +156,15 @@ class TrafficModeView:
|
||||
message: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RoutesResolveSummaryView:
|
||||
available: bool
|
||||
text: str
|
||||
recheck_text: str
|
||||
color: str
|
||||
recheck_color: str
|
||||
|
||||
|
||||
# ---------------------------
|
||||
# Controller
|
||||
# ---------------------------
|
||||
@@ -605,6 +614,10 @@ class DashboardController:
|
||||
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))
|
||||
@@ -617,6 +630,70 @@ class DashboardController:
|
||||
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))
|
||||
q_hits = int(pairs.get("quarantine_hits", 0))
|
||||
dns_attempts = int(pairs.get("dns_attempts", 0))
|
||||
dns_timeout = int(pairs.get("dns_timeout", 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"quarantine_hits={q_hits} | dns_timeout={dns_timeout} | attempts={dns_attempts}"
|
||||
)
|
||||
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"
|
||||
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 traffic_mode_view(self) -> TrafficModeView:
|
||||
st: TrafficModeStatus = self.client.traffic_mode_get()
|
||||
return TrafficModeView(
|
||||
@@ -884,6 +961,7 @@ class DashboardController:
|
||||
timeout_ms: int = 1800,
|
||||
attempts: int = 1,
|
||||
concurrency: int = 6,
|
||||
profile: str = "load",
|
||||
) -> DNSBenchmarkResponse:
|
||||
return self.client.dns_benchmark(
|
||||
upstreams=upstreams,
|
||||
@@ -891,6 +969,7 @@ class DashboardController:
|
||||
timeout_ms=timeout_ms,
|
||||
attempts=attempts,
|
||||
concurrency=concurrency,
|
||||
profile=profile,
|
||||
)
|
||||
|
||||
def dns_status_view(self) -> DNSStatus:
|
||||
|
||||
@@ -361,6 +361,11 @@ class MainWindow(QMainWindow):
|
||||
RU: Делает DNS-запросы wildcard-доменов, чтобы заранее наполнить agvpn_dyn4.""")
|
||||
self.btn_routes_prewarm.clicked.connect(self.on_smartdns_prewarm)
|
||||
relay_row.addWidget(self.btn_routes_prewarm)
|
||||
self.btn_routes_precheck_debug = QPushButton("Debug precheck now")
|
||||
self.btn_routes_precheck_debug.setToolTip("""EN: Debug helper. Arms one-shot resolver precheck and requests routes restart now.
|
||||
RU: Отладочный helper. Включает one-shot precheck резолвера и запрашивает restart routes.""")
|
||||
self.btn_routes_precheck_debug.clicked.connect(self.on_routes_precheck_debug)
|
||||
relay_row.addWidget(self.btn_routes_precheck_debug)
|
||||
relay_row.addStretch(1)
|
||||
traffic_layout.addLayout(relay_row)
|
||||
|
||||
@@ -383,6 +388,18 @@ RU: Агрессивный режим дополнительно дергает
|
||||
self.lbl_traffic_mode_diag.setStyleSheet("color: gray;")
|
||||
traffic_layout.addWidget(self.lbl_traffic_mode_diag)
|
||||
|
||||
self.lbl_routes_resolve_summary = QLabel("Resolve summary: —")
|
||||
self.lbl_routes_resolve_summary.setToolTip("""EN: Parsed from latest 'resolve summary' trace line.
|
||||
RU: Берется из последней строки 'resolve summary' в trace.""")
|
||||
self.lbl_routes_resolve_summary.setStyleSheet("color: gray;")
|
||||
traffic_layout.addWidget(self.lbl_routes_resolve_summary)
|
||||
|
||||
self.lbl_routes_recheck_summary = QLabel("Timeout recheck: —")
|
||||
self.lbl_routes_recheck_summary.setToolTip("""EN: Hidden timeout-recheck counters included in resolve summary.
|
||||
RU: Счетчики скрытого timeout-recheck из итогового resolve summary.""")
|
||||
self.lbl_routes_recheck_summary.setStyleSheet("color: gray;")
|
||||
traffic_layout.addWidget(self.lbl_routes_recheck_summary)
|
||||
|
||||
layout.addWidget(traffic_group)
|
||||
|
||||
# --- NFT progress (agvpn4) ---
|
||||
@@ -1083,6 +1100,11 @@ RU: Источник wildcard IP: резолвер, runtime nftset SmartDNS, и
|
||||
t.iface_reason,
|
||||
t.message,
|
||||
)
|
||||
rs = self.ctrl.routes_resolve_summary_view()
|
||||
self.lbl_routes_resolve_summary.setText(rs.text)
|
||||
self.lbl_routes_resolve_summary.setStyleSheet(f"color: {rs.color};")
|
||||
self.lbl_routes_recheck_summary.setText(rs.recheck_text)
|
||||
self.lbl_routes_recheck_summary.setStyleSheet(f"color: {rs.recheck_color};")
|
||||
self._safe(work, title="Routes error")
|
||||
|
||||
def refresh_dns_tab(self) -> None:
|
||||
@@ -1423,6 +1445,19 @@ RU: Источник wildcard IP: резолвер, runtime nftset SmartDNS, и
|
||||
self.refresh_status_tab()
|
||||
self._safe(work, title="Traffic mode test error")
|
||||
|
||||
def on_routes_precheck_debug(self) -> None:
|
||||
def work():
|
||||
res = self.ctrl.routes_precheck_debug(run_now=True)
|
||||
txt = (res.pretty_text or "").strip()
|
||||
if res.ok:
|
||||
QMessageBox.information(self, "Resolve precheck debug", txt or "OK")
|
||||
else:
|
||||
QMessageBox.critical(self, "Resolve precheck debug", txt or "ERROR")
|
||||
self.refresh_routes_tab()
|
||||
self.refresh_status_tab()
|
||||
self.refresh_trace_tab()
|
||||
self._safe(work, title="Resolve precheck debug error")
|
||||
|
||||
def on_toggle_timer(self) -> None:
|
||||
def work():
|
||||
enabled = self.chk_timer.isChecked()
|
||||
|
||||
Reference in New Issue
Block a user