Skip to content

Update redfish grafana dashboard. - #2518

Open
claudia-lola wants to merge 1 commit into
stackhpc/2026.1from
claudia-redfish-dashboard
Open

Update redfish grafana dashboard.#2518
claudia-lola wants to merge 1 commit into
stackhpc/2026.1from
claudia-redfish-dashboard

Conversation

@claudia-lola

@claudia-lola claudia-lola commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Add by () to expressions to stop double counting of metrics, without loss of information.

  • redfish_system_power_state returns values for the same server, but multiple system_id. This causes duplication for Redfish Up and Powered On panels.
  • redfish_chassis_health returns values for the same server, but multiple chassis_id. This causes duplication of Chassis status and Unhealthy Nodes and Chassis Status panels.
  • Chassis Status displays max value for all returned chassis_id
  • General Health panel for specific server would show critical or unhealthy if one chassis_id returned that value. Now takes an average of all returned chassis_id from metric redfish_chassis_health.

@claudia-lola
claudia-lola requested a review from a team as a code owner August 18, 2026 09:31
@github-actions github-actions Bot added the waiting-review PR is waiting for a review label Aug 18, 2026
@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Updated Redfish Grafana queries to aggregate metrics by system_id or server. This prevents duplicate counts across systems and chassis. Chassis status now displays the maximum value, while server-specific general health uses the average across returned chassis.

Walkthrough

The Grafana Redfish dashboard now aggregates Prometheus results by system_id or server. Chassis health values use maximum or average aggregation, with rounding applied to the general-health display.

Changes

Redfish dashboard query aggregation

Layer / File(s) Summary
Dashboard metric aggregation
etc/kayobe/kolla/config/grafana/dashboards/openstack/redfish.json
Power-state queries group results by system_id. Chassis health queries aggregate results by server and round the general-health average.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: 🟡 Moderate · up to 4fec3

The dashboard may merge data from different servers when aggregating by system_id, causing Redfish Up and Powered On panels to report incorrect results; one health query may also include duplicate scrape data. Merge should wait for the aggregation fix and health-query filter verification.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.5.6)
etc/kayobe/kolla/config/grafana/dashboards/openstack/redfish.json

File contains syntax errors that prevent linting: Line 1: unexpected character %; Line 1: unexpected character %; Line 2: End of file expected; Line 3588: End of file expected; Line 3588: unexpected character %; Line 3588: unexpected character %


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ac4adfc3-131f-46d9-b99c-92e7c32071ec

📥 Commits

Reviewing files that changed from the base of the PR and between 4fd9e9b and 4fec31c.

📒 Files selected for processing (1)
  • etc/kayobe/kolla/config/grafana/dashboards/openstack/redfish.json

Included review availability: Your plan includes up to 1 review per rolling hour; 0 remain after this review.

📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: stackhpc/check
  • GitHub Check: Tox releasenotes with Python 3.12
  • GitHub Check: Ansible 2.20 lint with Python 3.12
  • GitHub Check: Ansible 2.19 lint with Python 3.12
🔇 Additional comments (2)
etc/kayobe/kolla/config/grafana/dashboards/openstack/redfish.json (2)

330-330: LGTM!

Also applies to: 900-900


3086-3086: 🗄️ Data Integrity & Integration

Apply the exporter-job filter to the averaged health query.

Line 3086 omits job!="redfish-exporter-collectlog", unlike the other redfish_chassis_health queries in this dashboard. If that job exposes the same metric, avg includes both scrape targets and the panel still double-counts chassis. Prometheus avg includes every matching sample. (prometheus.io)

Add the matcher and verify the result with count by (job) (redfish_chassis_health{server=~"$server"}).

},
"editorMode": "code",
"expr": "count(redfish_system_power_state{group=\"$group\", job!=\"redfish-exporter-collectlog\"})",
"expr": "count(redfish_system_power_state{group=\"$group\", job!=\"redfish-exporter-collectlog\"}) by (system_id)",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
file='etc/kayobe/kolla/config/grafana/dashboards/openstack/redfish.json'
printf '%s\n' '--- relevant dashboard sections ---'
sed -n '80,125p;165,210p;300,345p;875,920p;3055,3110p' "$file"
printf '%s\n' '--- panel options and query references ---'
rg -n -C 3 '"title"|"reduceOptions"|"calcs"|"values"|"expr": ".*redfish_(system_power_state|chassis_health)' "$file"

Repository: stackhpc/stackhpc-kayobe-config

Length of output: 21416


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- redfish metric definitions and label usage ---'
rg -n -S -C 4 'redfish_system_power_state|system_id|redfish_chassis_health' . \
  -g '!etc/kayobe/kolla/config/grafana/dashboards/openstack/redfish.json' \
  -g '!*.lock' -g '!*.min.*' | head -n 500
printf '%s\n' '--- dashboard panels containing server-count or system-id aggregations ---'
rg -n -S -C 3 'count\(.*redfish_system_power_state|by \(server\)|by \(system_id\)|title": ".*(Up|Powered|Node|Health|Count)' \
  etc/kayobe/kolla/config/grafana/dashboards/openstack

Repository: stackhpc/stackhpc-kayobe-config

Length of output: 31094


🌐 Web query:

stackhpc redfish exporter redfish_system_power_state system_id server metric

💡 Result:

The metric redfish_system_power_state is commonly used in various Redfish Prometheus exporter implementations to report the power status of a managed server (ComputerSystem) [1]. It typically represents the current power state retrieved from the Redfish API (e.g., /redfish/v1/Systems/{system_id}) [2]. Key details regarding this metric include: Implementation and Usage - Metric Purpose: It is a gauge metric [3][2] that reflects whether a system is powered on or off. Implementations often map the Redfish PowerState string (e.g., "On", "Off") to numerical values (e.g., "On" = 1, "Off" = 0) [2]. - Identification: When exported to Prometheus, the metric is generally associated with labels that identify the specific server, such as the system_id, target address, or host name [3][4][2]. - Context: While different projects use slightly different naming conventions (e.g., redfish_system_power_state [1] vs. redfish_powerstate [3][2]), they all serve the purpose of monitoring out-of-band hardware power status [5][3]. StackHPC Specifics - The stackhpc/redfish_exporter is a Prometheus exporter designed to query Redfish-based servers (e.g., Dell, HPE, Supermicro) [5]. - In the context of the StackHPC Kayobe configuration, the exporter is enabled and configured to scrape BMC targets at specified intervals [6]. The specific metrics exposed depend on the version and configuration of the exporter deployed, as multiple open-source implementations exist under similar names (e.g., stackhpc [5], sapcc [3], dasbaum-ch [4]). To verify the exact metrics available in your specific deployment, you can query the /metrics endpoint of your running exporter: curl http://<redfish_exporter_host>:9610/metrics [5]

Citations:


🌐 Web query:

Prometheus aggregation operators count by label documentation

💡 Result:

In Prometheus (PromQL), aggregation operators such as count are used to aggregate elements of an instant vector [1][2]. To count by specific labels, you use the by or without clauses [1][2]. Aggregation Syntax Aggregation operators can be used with a by or without clause to control label preservation [1][3]: [without|by ()] () or () [without|by ()] by (): This clause preserves only the labels listed in the parentheses [1][2]. Even if other labels have identical values across all elements, they are dropped from the result [1][3]. without (): This clause removes the listed labels from the output, while preserving all other original labels [1][2]. Example To count the number of time series (e.g., running instances) grouped by a specific label such as app: count by (app) (instance_cpu_time_ns) [4] Other Relevant Operators count(v): Returns the number of elements in the vector [1][2]. count_values(l, v): Outputs one time series per unique sample value in v, creating a new label l whose value is the unique sample value and whose time series value is the count of how many times that sample value appeared [1][2]. count_over_time(v): A separate function (not an aggregation operator) that counts all samples for each series within a specified range vector [5][6]. Top Results: [1][2][4]

Citations:


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
import json, urllib.request
base = "https://api.github.com/repos/stackhpc/redfish_exporter"
for path in ("/git/trees/main?recursive=1", "/git/trees/master?recursive=1"):
    try:
        with urllib.request.urlopen(base + path, timeout=10) as r:
            data = json.load(r)
        print("tree", path, "status", data.get("sha"))
        for item in data.get("tree", []):
            name = item.get("path", "")
            if any(x in name.lower() for x in ("collector", "metric", "redfish")):
                print(name)
        break
    except Exception as exc:
        print("tree lookup failed", path, type(exc).__name__, str(exc))
PY
printf '%s\n' '--- repository references if present locally ---'
rg -n -S 'stackhpc/redfish_exporter|redfish_exporter' . -g '!*.json' -g '!*.lock' | head -n 200

Repository: stackhpc/stackhpc-kayobe-config

Length of output: 4595


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- local scrape labels and exporter configuration ---'
cat -n etc/kayobe/kolla/config/prometheus/prometheus.yml.d/60-redfish.yml
cat -n etc/kayobe/containers/redfish_exporter/redfish_exporter.yml
printf '%s\n' '--- configured image and version references ---'
rg -n -S -C 3 'jenningsloy318|redfish_exporter.*image|redfish-exporter|redfish_exporter.*version|redfish_exporter.*tag' etc doc releasenotes
printf '%s\n' '--- public source references ---'
for url in \
  https://raw.githubusercontent.com/jenningsloy318/redfish_exporter/master/redfish_exporter.py \
  https://raw.githubusercontent.com/jenningsloy318/redfish_exporter/main/redfish_exporter.py \
  https://raw.githubusercontent.com/jenningsloy318/redfish_exporter/master/collector.py \
  https://raw.githubusercontent.com/jenningsloy318/redfish_exporter/main/collector.py; do
  echo "URL: $url"
  curl -L --max-time 10 -k -sS "$url" | rg -n -C 5 'system_power_state|system_id|chassis_health|Metric|GaugeMetricFamily' | head -n 120 || true
done

Repository: stackhpc/stackhpc-kayobe-config

Length of output: 2885


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
import json, ssl, urllib.request
ctx = ssl._create_unverified_context()
repos = [
    "stackhpc/redfish-exporter",
    "stackhpc/redfish_exporter",
    "jenningsloy318/redfish_exporter",
]
for repo in repos:
    url = f"https://api.github.com/repos/{repo}/git/trees/main?recursive=1"
    try:
        req = urllib.request.Request(url, headers={"User-Agent": "review-inspection"})
        with urllib.request.urlopen(req, context=ctx, timeout=15) as r:
            data = json.load(r)
        print(f"REPO {repo} TREE {data.get('sha')}")
        for item in data.get("tree", []):
            path = item.get("path", "")
            if path.endswith((".py", ".go", ".yml", ".yaml")):
                print(path)
    except Exception as exc:
        print(f"REPO {repo} ERROR {type(exc).__name__}: {exc}")
PY

Repository: stackhpc/stackhpc-kayobe-config

Length of output: 631


🏁 Script executed:

#!/bin/bash
set -e
for path in collector/system_collector.go collector/redfish_collector.go collector/common_collector.go README.md; do
  url="https://raw.githubusercontent.com/stackhpc/redfish_exporter/main/$path"
  echo "--- $path ---"
  curl -L --max-time 15 -k -sS "$url" \
    | rg -n -C 8 'system_power_state|system_id|server|MetricDesc|NewDesc|Describe|Collect' \
    | head -n 300 || true
done
printf '%s\n' '--- tags and release references ---'
curl -L --max-time 15 -k -sS \
  -H 'Accept: application/vnd.github+json' \
  https://api.github.com/repos/stackhpc/redfish_exporter/tags \
  | head -c 12000

Repository: stackhpc/stackhpc-kayobe-config

Length of output: 12666


🏁 Script executed:

#!/bin/bash
set -e
file='etc/kayobe/kolla/config/grafana/dashboards/openstack/redfish.json'
printf '%s\n' '--- changed query context ---'
git diff --unified=8 -- "$file" | sed -n '1,220p'
printf '%s\n' '--- exporter system collector implementation ---'
curl -L --max-time 15 -k -sS \
  https://raw.githubusercontent.com/stackhpc/redfish_exporter/main/collector/system_collector.go \
  | sed -n '130,172p'
printf '%s\n' '--- deterministic aggregation model ---'
python3 - <<'PY'
from collections import Counter
series = [
    {"server": "srv-a", "system_id": "1", "value": 1},
    {"server": "srv-b", "system_id": "1", "value": 1},
    {"server": "srv-c", "system_id": "2", "value": 1},
]
by_system = Counter(s["system_id"] for s in series)
by_server = Counter(s["server"] for s in series)
print("count(metric) by (system_id):", dict(by_system))
print("mean of grouped values:", sum(by_system.values()) / len(by_system))
print("count(metric) by (server):", dict(by_server))
print("count(count(metric) by (server)):", len(by_server))
on = Counter(s["server"] for s in series if s["value"] == 1)
print("count(count(metric == 1) by (server)):", len(on))
PY

Repository: stackhpc/stackhpc-kayobe-config

Length of output: 2987


Count servers in the overview panels.

The exporter emits redfish_system_power_state once per Redfish system, while the scrape configuration labels each target with server. by (system_id) can merge systems from different servers and returns one value per system_id; the stat panels then display or average those values.

Use count(count(redfish_system_power_state{...}) by (server)) at line 109 and count(count(redfish_system_power_state{...} == 1) by (server)) at line 192.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-review PR is waiting for a review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant