release: nexus btpanel session fix and app-v2
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
"""Fleet-wide metric aggregation from Agent heartbeat payloads."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
|
||||
def _safe_pct(value: Any) -> float | None:
|
||||
if value is None:
|
||||
return None
|
||||
try:
|
||||
num = float(value)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
if num < 0 or num > 100:
|
||||
return None
|
||||
return num
|
||||
|
||||
|
||||
def _metric_from_system_info(system_info: dict[str, Any], primary: str, fallback: str) -> float | None:
|
||||
return _safe_pct(system_info.get(primary) if system_info.get(primary) is not None else system_info.get(fallback))
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FleetMetricAggregate:
|
||||
online_count: int
|
||||
sample_count: int
|
||||
cpu_avg: int | None
|
||||
mem_avg: int | None
|
||||
disk_avg: int | None
|
||||
|
||||
|
||||
def aggregate_fleet_metrics(heartbeats: list[dict[str, Any]]) -> FleetMetricAggregate:
|
||||
"""Aggregate Redis heartbeat hash rows (is_online + system_info JSON string)."""
|
||||
online_count = 0
|
||||
cpu_vals: list[float] = []
|
||||
mem_vals: list[float] = []
|
||||
disk_vals: list[float] = []
|
||||
|
||||
for row in heartbeats:
|
||||
if row.get("is_online") in (True, "True"):
|
||||
online_count += 1
|
||||
raw = row.get("system_info") or "{}"
|
||||
try:
|
||||
info = json.loads(raw) if isinstance(raw, str) else (raw or {})
|
||||
except json.JSONDecodeError:
|
||||
info = {}
|
||||
if not isinstance(info, dict):
|
||||
continue
|
||||
cpu = _metric_from_system_info(info, "cpu_usage", "cpu")
|
||||
mem = _metric_from_system_info(info, "mem_usage", "mem")
|
||||
disk = _metric_from_system_info(info, "disk_usage", "disk")
|
||||
if cpu is not None:
|
||||
cpu_vals.append(cpu)
|
||||
if mem is not None:
|
||||
mem_vals.append(mem)
|
||||
if disk is not None:
|
||||
disk_vals.append(disk)
|
||||
|
||||
sample_count = max(len(cpu_vals), len(mem_vals), len(disk_vals))
|
||||
|
||||
def _avg(vals: list[float]) -> int | None:
|
||||
if not vals:
|
||||
return None
|
||||
return round(sum(vals) / len(vals))
|
||||
|
||||
return FleetMetricAggregate(
|
||||
online_count=online_count,
|
||||
sample_count=sample_count,
|
||||
cpu_avg=_avg(cpu_vals),
|
||||
mem_avg=_avg(mem_vals),
|
||||
disk_avg=_avg(disk_vals),
|
||||
)
|
||||
Reference in New Issue
Block a user