mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 22:30:57 +00:00
Merge branch 'pratik/otel-phase9-metric-gap-fill' into pratik/otel-phase10-workload-validation
This commit is contained in:
@@ -1545,11 +1545,11 @@
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 115
|
||||
"y": 121
|
||||
},
|
||||
"collapsed": false,
|
||||
"panels": [],
|
||||
"id": 27
|
||||
"id": 38
|
||||
},
|
||||
{
|
||||
"title": "Job Queue Backlog and Deferred by Type",
|
||||
@@ -1559,7 +1559,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 116
|
||||
"y": 122
|
||||
},
|
||||
"options": {
|
||||
"tooltip": {
|
||||
@@ -1608,7 +1608,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 116
|
||||
"y": 122
|
||||
},
|
||||
"options": {
|
||||
"tooltip": {
|
||||
@@ -1649,7 +1649,7 @@
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 124
|
||||
"y": 130
|
||||
},
|
||||
"collapsed": false,
|
||||
"panels": [],
|
||||
@@ -1663,7 +1663,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 125
|
||||
"y": 131
|
||||
},
|
||||
"options": {
|
||||
"tooltip": {
|
||||
@@ -1735,7 +1735,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 125
|
||||
"y": 131
|
||||
},
|
||||
"options": {
|
||||
"tooltip": {
|
||||
@@ -1784,7 +1784,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 133
|
||||
"y": 139
|
||||
},
|
||||
"options": {
|
||||
"tooltip": {
|
||||
@@ -1844,7 +1844,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 133
|
||||
"y": 139
|
||||
},
|
||||
"options": {
|
||||
"tooltip": {
|
||||
@@ -1893,7 +1893,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 141
|
||||
"y": 147
|
||||
},
|
||||
"options": {
|
||||
"tooltip": {
|
||||
@@ -1949,7 +1949,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 141
|
||||
"y": 147
|
||||
},
|
||||
"options": {
|
||||
"tooltip": {
|
||||
@@ -1998,7 +1998,7 @@
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 149
|
||||
"y": 155
|
||||
},
|
||||
"options": {
|
||||
"tooltip": {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Dashboard lint: cumulative metrics must be rate()-wrapped; tier filters present."""
|
||||
"""Dashboard lint: cumulative metrics rate()-wrapped; tier filters; sane panel grid."""
|
||||
|
||||
import json, re, sys
|
||||
|
||||
GRID_COLUMNS = 24
|
||||
|
||||
# Prometheus gauges that hold a CUMULATIVE total -> must be rate()/increase()-wrapped.
|
||||
CUMULATIVE_PREFIXES = (
|
||||
"total_bytes_",
|
||||
@@ -55,6 +57,55 @@ def iter_panels(dash):
|
||||
yield sub
|
||||
|
||||
|
||||
def check_layout(path, dash):
|
||||
"""Duplicate panel ids and grid collisions -- both break Grafana's loader.
|
||||
|
||||
Grafana keys panels by id when it builds the dashboard model, so two panels
|
||||
sharing an id make the load non-deterministic. Overlapping gridPos rectangles
|
||||
have no valid layout. Only top-level panels are checked: panels nested inside
|
||||
a collapsed row do not occupy the outer grid.
|
||||
"""
|
||||
errs = []
|
||||
top = dash.get("panels", []) or []
|
||||
|
||||
seen_ids = {}
|
||||
for p in top:
|
||||
pid = p.get("id")
|
||||
if pid is None:
|
||||
continue
|
||||
if pid in seen_ids:
|
||||
errs.append(
|
||||
f"{path}: duplicate panel id {pid}: "
|
||||
f"[{seen_ids[pid]}] and [{p.get('title', '<untitled>')}]"
|
||||
)
|
||||
else:
|
||||
seen_ids[pid] = p.get("title", "<untitled>")
|
||||
|
||||
# Mark every grid cell each panel covers; a second claim on a cell is a collision.
|
||||
owner = {}
|
||||
for p in top:
|
||||
g = p.get("gridPos") or {}
|
||||
x, y = g.get("x", 0), g.get("y", 0)
|
||||
w, h = g.get("w", 0), g.get("h", 0)
|
||||
title = p.get("title", "<untitled>")
|
||||
if x + w > GRID_COLUMNS:
|
||||
errs.append(
|
||||
f"{path} [{title}]: spans past the {GRID_COLUMNS}-column grid (x={x}, w={w})"
|
||||
)
|
||||
clashed = set()
|
||||
for cy in range(y, y + h):
|
||||
for cx in range(x, min(x + w, GRID_COLUMNS)):
|
||||
prev = owner.get((cy, cx))
|
||||
if prev is None:
|
||||
owner[(cy, cx)] = title
|
||||
elif prev not in clashed:
|
||||
clashed.add(prev)
|
||||
errs.append(
|
||||
f"{path} [{title}]: grid overlap with [{prev}] at y={cy} x={cx}"
|
||||
)
|
||||
return errs
|
||||
|
||||
|
||||
def expr_is_wrapped(expr):
|
||||
return (
|
||||
"rate(" in expr
|
||||
@@ -70,6 +121,7 @@ def check(path, forbid_5m):
|
||||
dash = json.load(open(path))
|
||||
except Exception as e:
|
||||
return [f"{path}: INVALID JSON: {e}"]
|
||||
errs += check_layout(path, dash)
|
||||
for p in iter_panels(dash):
|
||||
title = p.get("title", "<untitled>")
|
||||
for tg in p.get("targets", []) or []:
|
||||
|
||||
Reference in New Issue
Block a user