Set-OPS-Public/roles/serveur_grafana/templates/tableau-role.json.j2
Daniel Allaire 311f33d6f6 grafana : les panneaux declares deviennent des tableaux
La moitie qui manquait. Les roles declaraient leurs panneaux, Prometheus en
derivait ses cibles, les expressions repondaient, et rien ne les assemblait.

serveur_grafana lit les memes meta/metriques.yml que serveur_prometheus et en
assemble un tableau par role — la raison du panneau devient sa description dans
Grafana. Moisson des tableaux orphelins, prefixe setops-role- pour ne jamais
toucher un tableau ecrit a la main, JSON valide avant d etre pose (Grafana ne
tombe pas sur un tableau illisible : il le saute en silence).

client_metrique declare quatre panneaux sans exportateur — le cas symetrique.
Celui qui compte : la memoire disponible, depuis que le ballon est actif.

Eprouve sur le site : les deux tableaux charges dans le stockage unifie, un
temoin orphelin retire, les quatre panneaux de flotte a 10 series chacun.

Un panneau etait faux — /var/lib/lxcfs rapporte toujours zero octet libre et un
min() en meurt. Corrige en liste blanche : un montage inconnu manque au graphe,
ce qui se voit, au lieu de l ecraser.

P77 garde la table des unites : une unite inventee retombe sur short, lisible et
fausse. 76 OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-14 16:32:30 -04:00

77 lines
3.4 KiB
Django/Jinja

{#- GENERE par Set-OPS (role serveur_grafana). Ne pas editer a la main.
UN TABLEAU PAR ROLE, ASSEMBLE DEPUIS SON `meta/metriques.yml`.
C'EST LA MOITIE QUI MANQUAIT. Les roles declaraient leurs panneaux, Prometheus
collectait les series, les expressions repondaient — et rien ne les assemblait. Un
panneau declare que personne ne peut regarder n'est pas une observabilite, c'est une
intention.
POURQUOI UN TABLEAU PAR ROLE ET PAS UN GRAND. Parce que la question qu'on se pose est
« comment va PostgreSQL », pas « comment va la flotte » — celle-la est deja repondue
par Icinga, et par le tableau des journaux. Un tableau par role suit la meme ligne de
partage que tout le reste ici : le role est l'unite qui declare, donc l'unite qui
s'affiche.
LA `raison` DEVIENT LA DESCRIPTION DU PANNEAU. Elle est le seul champ obligatoire qui
ne produit aucun pixel de graphe, et c'est le plus important : sans elle, celui qui
regarde a six mois de distance voit une courbe sans savoir ce qu'elle annonce. Grafana
l'affiche au survol du titre.
-#}
{%- set roles_unites = serveur_grafana_unites -%}
{
"uid": {{ ("setops-" ~ tableau_role | replace("serveur_", "") | replace("_", "-")) | truncate(40, true, "") | tojson }},
"title": {{ ("Set-OPS — " ~ tableau_role | replace("serveur_", "") | replace("_", " ")) | tojson }},
"tags": ["set-ops", "derive", {{ tableau_role | tojson }}],
"timezone": "browser",
"refresh": "1m",
"time": { "from": "now-6h", "to": "now" },
"schemaVersion": 39,
"version": 1,
"templating": {
"list": [
{
"name": "ds_prom",
"label": "Source Prometheus",
"type": "datasource",
"query": "prometheus",
"hide": 2,
"current": {}
}
]
},
"panels": [
{%- for p in tableau_panneaux %}
{
"id": {{ loop.index }},
"type": "timeseries",
"title": {{ p.titre | tojson }},
{#- LA RAISON, TELLE QUELLE. On ne la resume pas : c'est le texte que le role a
ecrit pour expliquer ce que la courbe annonce. #}
"description": {{ (p.raison | default("") | trim) | tojson }},
"datasource": { "type": "prometheus", "uid": "${ds_prom}" },
{#- DEUX PAR RANGEE. Une expression PromQL rend souvent plusieurs series ; a pleine
largeur on ne compare rien, a un quart de largeur on ne lit rien. #}
"gridPos": { "h": 8, "w": 12, "x": {{ 0 if loop.index0 % 2 == 0 else 12 }}, "y": {{ (loop.index0 // 2) * 8 }} },
"fieldConfig": {
"defaults": {
{#- L'UNITE VIENT DU ROLE, TRADUITE EN UNITE GRAFANA. Un `unite:` inconnu
retombe sur `short` — et P77 refuse de laisser ce cas passer inapercu. #}
"unit": {{ roles_unites.get(p.unite | default("") | string, "short") | tojson }},
"custom": { "drawStyle": "line", "fillOpacity": 10, "showPoints": "never" }
}
},
"options": { "legend": { "displayMode": "list", "placement": "bottom" } },
"targets": [
{
"refId": "A",
"datasource": { "type": "prometheus", "uid": "${ds_prom}" },
{#- L'EXPRESSION EST CELLE DU ROLE, non reecrite. Repliee sur une ligne parce
que `>-` en YAML laisse des retours que JSON refuse. #}
"expr": {{ (p.expr | trim | replace("\n", " ")) | regex_replace("\\s+", " ") | tojson }}
}
]
}{{ "," if not loop.last else "" }}
{%- endfor %}
]
}