From ba1a12d173957c86497ba403913bdbf433eff427 Mon Sep 17 00:00:00 2001 From: charliez Date: Mon, 3 Aug 2026 20:58:35 -0700 Subject: [PATCH 1/3] Label y-axis units on stress-test CPU and inode panels Fixes #69944 The CPU Usage panels (Master, Minion 1-3, API) use Grafana's percentunit, a 0-1 ratio where 1.0 == 1 full CPU core -- matching rate(container_cpu_usage_seconds_total[...]) semantics from cAdvisor. Without a label, a value like 1.2 is easy to misread as "1.2% of the host" rather than 1.2 CPU cores. The Minion Inodes panels use Grafana's "short" unit (a plain count) and also had no y-axis label. render_panels.py already special-cases bytes-family units to convert to MB and label the axis; this follows the same pattern for percentunit and short so every rendered panel states what its numbers mean. --- .gitignore | 1 + tests/monitoring/render_panels.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/.gitignore b/.gitignore index 21257ad4d499..6d7607c5d284 100644 --- a/.gitignore +++ b/.gitignore @@ -166,3 +166,4 @@ nox.*.tar.xz /.gemini venv311/ venv312/ +.cursor-ai/ diff --git a/tests/monitoring/render_panels.py b/tests/monitoring/render_panels.py index 00a46a159fc6..70a297410489 100644 --- a/tests/monitoring/render_panels.py +++ b/tests/monitoring/render_panels.py @@ -122,6 +122,14 @@ def _bytes_unit(unit_hint: str) -> bool: return unit_hint.lower() in ("bytes", "decbytes", "kbytes", "mbytes", "gbytes") +def _is_percentunit(unit_hint: str) -> bool: + return unit_hint.lower() == "percentunit" + + +def _is_count_unit(unit_hint: str) -> bool: + return unit_hint.lower() == "short" + + def render_panel(panel: dict, end_ts: float) -> plt.Figure | None: """Return a matplotlib Figure for ``panel``, or ``None`` if no series.""" targets = panel.get("targets") or [] @@ -130,6 +138,8 @@ def render_panel(panel: dict, end_ts: float) -> plt.Figure | None: unit_hint = panel.get("fieldConfig", {}).get("defaults", {}).get("unit") or "" is_bytes = _bytes_unit(unit_hint) + is_percentunit = _is_percentunit(unit_hint) + is_count = _is_count_unit(unit_hint) fig, ax = plt.subplots(figsize=(11, 4)) series_count = 0 @@ -165,6 +175,14 @@ def render_panel(panel: dict, end_ts: float) -> plt.Figure | None: ax.set_title(panel.get("title") or "panel", fontsize=11) if is_bytes: ax.set_ylabel("MB") + elif is_percentunit: + # Grafana's percentunit is a 0-1 ratio (1.0 == 1 CPU core, not 1% of + # the host); rate(container_cpu_usage_seconds_total[...]) values here + # are already in that ratio, so label explicitly to avoid confusing + # "1.2" with "1.2% of the host" instead of 1.2 CPU cores. + ax.set_ylabel("CPU cores (1.0 = 1 core)") + elif is_count: + ax.set_ylabel("count") ax.xaxis.set_major_formatter(DateFormatter("%H:%M")) ax.tick_params(axis="x", rotation=30, labelsize=8) ax.tick_params(axis="y", labelsize=8) From f00f1e6f401e04e962e48d7425cf5ba74706ddac Mon Sep 17 00:00:00 2001 From: charliez Date: Wed, 5 Aug 2026 16:03:07 -0700 Subject: [PATCH 2/3] Revert unrelated .gitignore entry .cursor-ai/ was accidentally picked up while preparing this branch and is unrelated to the y-axis label fix. --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6d7607c5d284..21257ad4d499 100644 --- a/.gitignore +++ b/.gitignore @@ -166,4 +166,3 @@ nox.*.tar.xz /.gemini venv311/ venv312/ -.cursor-ai/ From ab929b7cb63ab05a6c0d88e8f05b2b5faa4d7b56 Mon Sep 17 00:00:00 2001 From: charliez Date: Wed, 5 Aug 2026 17:05:48 -0700 Subject: [PATCH 3/3] Disambiguate the "short"-unit y-axis label by panel title The "short" unit covers both inode counts (Minion Inodes) and FD/process counts (Master & API Resource Usage), which had been collapsed into the same generic "count" label. Label each by what it actually counts instead. --- tests/monitoring/render_panels.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/monitoring/render_panels.py b/tests/monitoring/render_panels.py index 70a297410489..b80fec354467 100644 --- a/tests/monitoring/render_panels.py +++ b/tests/monitoring/render_panels.py @@ -182,7 +182,16 @@ def render_panel(panel: dict, end_ts: float) -> plt.Figure | None: # "1.2" with "1.2% of the host" instead of 1.2 CPU cores. ax.set_ylabel("CPU cores (1.0 = 1 core)") elif is_count: - ax.set_ylabel("count") + # "short" also covers FD/process counts (Master & API Resource Usage) + # alongside inode counts -- disambiguate from the panel title so the + # label says what's actually being counted, not just "count". + title = (panel.get("title") or "").lower() + if "inode" in title: + ax.set_ylabel("inodes used") + elif "fd" in title or "process" in title: + ax.set_ylabel("count (FDs vs processes)") + else: + ax.set_ylabel("count") ax.xaxis.set_major_formatter(DateFormatter("%H:%M")) ax.tick_params(axis="x", rotation=30, labelsize=8) ax.tick_params(axis="y", labelsize=8)