From 5bb69a9109a469bcb0d071eac52481d70ce2d1b8 Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Wed, 28 Jan 2026 22:33:44 -0300 Subject: [PATCH] snapshot: include namespace/node pod summaries --- atlasbot/snapshot/builder.py | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/atlasbot/snapshot/builder.py b/atlasbot/snapshot/builder.py index a63d49c..0e3f7d2 100644 --- a/atlasbot/snapshot/builder.py +++ b/atlasbot/snapshot/builder.py @@ -75,6 +75,8 @@ def build_summary(snapshot: dict[str, Any] | None) -> dict[str, Any]: summary.update(_build_hardware(nodes_detail)) summary.update(_build_pods(metrics)) summary.update(_build_namespace_pods(snapshot)) + summary.update(_build_namespace_nodes(snapshot)) + summary.update(_build_node_pods(snapshot)) summary.update(_build_postgres(metrics)) summary.update(_build_hottest(metrics)) summary.update(_build_workloads(snapshot)) @@ -138,6 +140,20 @@ def _build_namespace_pods(snapshot: dict[str, Any]) -> dict[str, Any]: return {"namespace_pods": namespaces} +def _build_namespace_nodes(snapshot: dict[str, Any]) -> dict[str, Any]: + namespace_nodes = snapshot.get("namespace_nodes") + if not isinstance(namespace_nodes, list) or not namespace_nodes: + return {} + return {"namespace_nodes": namespace_nodes} + + +def _build_node_pods(snapshot: dict[str, Any]) -> dict[str, Any]: + node_pods = snapshot.get("node_pods") + if not isinstance(node_pods, list) or not node_pods: + return {} + return {"node_pods": node_pods} + + def _build_postgres(metrics: dict[str, Any]) -> dict[str, Any]: postgres = metrics.get("postgres_connections") if isinstance(metrics.get("postgres_connections"), dict) else {} if not postgres: @@ -307,6 +323,53 @@ def _append_namespace_pods(lines: list[str], summary: dict[str, Any]) -> None: lines.append("namespaces_top: " + "; ".join(parts)) +def _append_namespace_nodes(lines: list[str], summary: dict[str, Any]) -> None: + namespace_nodes = summary.get("namespace_nodes") + if not isinstance(namespace_nodes, list) or not namespace_nodes: + return + top = sorted( + (item for item in namespace_nodes if isinstance(item, dict)), + key=lambda item: (-int(item.get("pods_total") or 0), item.get("namespace") or ""), + )[:8] + parts = [] + for item in top: + namespace = item.get("namespace") + pods_total = item.get("pods_total") + primary = item.get("primary_node") + if namespace: + label = f"{namespace}={pods_total}" + if primary: + label = f"{label} (primary={primary})" + parts.append(label) + if parts: + lines.append("namespace_nodes_top: " + "; ".join(parts)) + + +def _append_node_pods(lines: list[str], summary: dict[str, Any]) -> None: + node_pods = summary.get("node_pods") + if not isinstance(node_pods, list) or not node_pods: + return + top = sorted( + (item for item in node_pods if isinstance(item, dict)), + key=lambda item: (-int(item.get("pods_total") or 0), item.get("node") or ""), + )[:8] + parts = [] + for item in top: + node = item.get("node") + pods_total = item.get("pods_total") + namespaces = item.get("namespaces_top") or [] + ns_label = "" + if namespaces: + ns_label = ", ".join([f"{name}={count}" for name, count in namespaces]) + if node: + label = f"{node}={pods_total}" + if ns_label: + label = f"{label} ({ns_label})" + parts.append(label) + if parts: + lines.append("node_pods_top: " + "; ".join(parts)) + + def _append_node_usage_stats(lines: list[str], summary: dict[str, Any]) -> None: metrics = summary.get("metrics") if isinstance(summary.get("metrics"), dict) else {} stats = metrics.get("node_usage_stats") if isinstance(metrics.get("node_usage_stats"), dict) else {} @@ -464,6 +527,8 @@ def summary_text(snapshot: dict[str, Any] | None) -> str: _append_hardware(lines, summary) _append_pods(lines, summary) _append_namespace_pods(lines, summary) + _append_namespace_nodes(lines, summary) + _append_node_pods(lines, summary) _append_node_usage_stats(lines, summary) _append_namespace_usage(lines, summary) _append_restarts(lines, summary)