Monitoring de raspberry, la suite.
Maintenant que l’on a instrumenté et analysé son infra, il est temps d’afficher tout ça dans un dashboard.
Il y a plusieurs façon d’e faire de la dataviz avec Warp 10.
- Coder sois-même son interface
- Utiliser un outil tout fait comme Grafana
- Utiliser WarpView pour se construire son dashboard
- Utiliser Discovery pour un dashboard as code
Nous utiliserons ici Discovery. Un dashboard as code a de multiples intérêts :
- versionner son dashboard
- le rendre dynamique en fonction de certains paramètres
- afficher certaines info quand on dépasse un seuil par exemple
- passer un graphique en rouge en fonction d’un KPI
- …
- le réutiliser simplement pour d’autres usages
- le modulariser
Mode d'emploi
Discovery est très simple à prendre en main. Il s’agit de passer une structure de donnée (que je vais détailler) en WarpScript entre 2 balises HTML.
<html>
<head>
<title>Raspi-1 dashboard</title>
</head>
<body>
<discovery-dashboard url="https://your warp 10/api/v0/exec">
// Le WarpScript va ici
</discovery-dashboard>
<!-- Les imports -->
<script nomodule src="https://unpkg.com/@senx/discovery-widgets/dist/discovery/discovery.js"></script>
<script type="module" src="https://unpkg.com/@senx/discovery-widgets/dist/discovery/discovery.esm.js"></script>
</body>
</html>
Le WarpScript doit produire la structure suivante :
- // dashboard
title
: Titre du dashboard (optionnel)description
: Description du dashboard (optionnel)options
: les options communes pour le dashboard (ce sont les mêmes que pour WarpView et ici aussi) (optionnel)autoRefresh
: période en seconde entre 2 refresh des données, -1 pour désactiver (par défaut) (optionnel)
tiles
: Liste des « tuiles » à afficher- // tuile
title
: Titre de la tuile (optionnel)x, y
: position x et y sur la grille (commencent à 0 et la grille fait 12 cases de large)w, h
: hauteur et largeur en nombre de cases sur la grille (la largeur max est de 12)- type : type de graphique (‘line’, ‘area’, ‘scatter’, ‘spline-area’, ‘spline’, ‘step’, ‘step-after’, ‘step-before’, ‘annotation’, ‘bar’, ‘display’, ‘image’, ‘map’, ‘gauge’, ‘circle’, ‘pie’, ‘plot’, ‘doughnut’, ‘rose’, ‘tabular’, ‘button’)
unit
: unité à afficher (optionnel)options
: les options communes pour le dashboard (ce sont les mêmes que pour WarpView et ici aussi) (optionnel)autoRefresh
: période en seconde entre 2 refresh des données, -1 pour désactiver (par défaut) (optionnel et ne concerne que ‘macro’)
data
/macro
data
: le code sera exécuté au moment de l’exécution de la structure du dashboardmacro
: le code sera exécuté par la tuile une fois affichée
Bon, un exemple sera plus parlant :
{
'title' 'Test'
'options' { 'autoRefresh' 10 } // l'ensemble du dashboard se rafraîchira toutes les 10 secondes
'tiles' [
{
'title' 'Utilisation de "macro"'
'x' 0 'y' 0 'w' 6 'h' 1
'options' { 'autoRefresh' 2 } // cette tuile se rafraîchira toutes les 2 secondes
'type' 'display'
'macro' <% // C'est une macro WarpScript
RAND 100.0 * ROUND
%>
}
{
'title' 'Utilisation de "data"'
'x' 6 'y' 0 'w' 6 'h' 1
'type' 'display'
'data' RAND 100.0 * ROUND
}
]
}
Notre dashboard
En utilisant ‘data’ on peut calculer au moment de la génération (ou du refresh) du dashboard, avec ‘macro’ c’est en ajax depuis la tuile.
Bon, cool, passons à nos métriques réalisés la dernière fois. On va utiliser ‘data’ car, ça nous permet d’utiliser des variables (genre, le token ou le host name) et on va rafraîchir le dashboard périodiquement.
Commençons par les variables :
// Variables
'your read token' 'token' STORE
'2592000000000' 'duration' STORE // 1 mois
'rasp1-1' 'hname' STORE
Puis le dashboad (sans les tuiles) :
{
'title' 'Raspi-1'
'options' { 'autoRefresh' 30 } // auto refresh de 30 secondes
'tiles' [
// On va ajouter les tuiles là !
]
}
Tuile d’affichage de l’espace disque :
{
'title' 'Disks'
'x' 0 'y' 0 'w' 3 'h' 1 'type' 'gauge' 'unit' 'Gb'
'data' [ $token '~linux.df.bytes.(capacity|free)' { 'hname' $hname 'device' '~/dev/.*' } NOW -1 ] FETCH 'gts' STORE
[ $gts bucketizer.last NOW 0 1 ] BUCKETIZE [ 'device' 'mountpoint' ] PARTITION 'gts' STORE
[] 'data' STORE
[] 'params' STORE
$gts KEYLIST <%
'k' STORE
{
$gts $k GET
<% 'g' STORE
$g NAME 'linux.df.bytes.' '' REPLACE
$g VALUES REVERSE 0 GET
%> FOREACH
} 'vals' STORE
$data {
'key' $k 'device' GET ' (' + $k 'mountpoint' GET + ')' +
'value' $vals 'capacity' GET $vals 'free' GET - 1024 / 1024 / 1024.0 / 100 * ROUND 100.0 /
} +! DROP
$params { 'maxValue' $vals 'capacity' GET 1024 / 1024 / 1024 / } +! DROP
%> ASREGS FOREACH
{ 'data' $data 'params' $params }
}
La tuile pour le réseau :
{
'title' 'Network'
'x' 0 'y' 1 'w' 3 'h' 1 'type' 'area'
'data' [ $token '~linux.proc.net.dev.(receive|transmit).bytes' { 'hname' $hname 'iface' 'eth0' } NOW $duration TOLONG ] FETCH
false RESETS 'gts' STORE
[
[ $gts [] 'linux.proc.net.dev.receive.bytes' filter.byclass ] FILTER
[ $gts [] 'linux.proc.net.dev.transmit.bytes' filter.byclass ] FILTER
] FLATTEN [ SWAP bucketizer.mean NOW 1 h 0 ] BUCKETIZE 'gts' STORE
[ $gts mapper.delta 1 0 0 ] MAP
}
La tuile pour la charge CPU :
{
'title' 'Load'
'x' 3 'y' 1 'w' 9 'h' 1 'type' 'area'
'data' <%
[ $token '~linux.proc.stat.userhz.(user|nice|system|idle|iowait)' { 'hname' $hname 'cpu' 'cpu' } NOW $duration TOLONG ] FETCH 'gts' STORE
[ $gts bucketizer.mean NOW 1 h 0 ] BUCKETIZE 'gts' STORE
[ $gts [] '~linux.proc.stat.userhz.(user|nice|system|idle|iowait)' filter.byclass ] FILTER [ SWAP [] reducer.sum ] REDUCE 0 GET 'cpuGTS' STORE
[ $gts [] 'linux.proc.stat.userhz.idle' filter.byclass ] FILTER 0 GET 'idleGTS' STORE
[ $gts [] '~linux.proc.stat.userhz.(iowait|idle)' filter.byclass ] FILTER [ SWAP [] reducer.sum ] REDUCE 0 GET 'iowaitGTS' STORE
[ $gts [] '~linux.proc.stat.userhz.(system|idle)' filter.byclass ] FILTER [ SWAP [] reducer.sum ] REDUCE 0 GET 'systemGTS' STORE
[
[ [ $cpuGTS $idleGTS ] [] <%
'd' STORE
$d 7 GET 0 GET 'cpu' STORE
$d 7 GET 1 GET 'idle' STORE
1000.0 $cpu $idle - * $cpu 5 + / 10 / 'v' STORE
[ $d 0 GET NaN NaN NaN $v ]
%> ASREGS MACROREDUCER ] REDUCE 0 GET 'CPU' RENAME
[ [ $iowaitGTS $idleGTS ] [] <%
'd' STORE
$d 7 GET 0 GET 'cpu' STORE
$d 7 GET 1 GET 'idle' STORE
1000.0 $cpu $idle - * $cpu 5 + / 10 / 'v' STORE
[ $d 0 GET NaN NaN NaN $v ]
%> ASREGS MACROREDUCER ] REDUCE 0 GET 'Disk IO' RENAME
[ [ $systemGTS $idleGTS ] [] <%
'd' STORE
$d 7 GET 0 GET 'cpu' STORE
$d 7 GET 1 GET 'idle' STORE
1000.0 $cpu $idle - * $cpu 5 + / 10 / 'v' STORE
[ $d 0 GET NaN NaN NaN $v ]
%> ASREGS MACROREDUCER ] REDUCE 0 GET 'System' RENAME
] %> <% [] %> <% %> TRY
}
Et enfin, la tuile pour la RAM/Swap :
{
'title' 'RAM/Swap'
'x' 3 'y' 0 'w' 9 'h' 1 'type' 'area'
'data' [ $token '~linux.proc.meminfo.(MemFree|SwapFree)' { 'hname' $hname } NOW $duration TOLONG ] FETCH 'gts' STORE
[ $gts bucketizer.mean NOW 1 h 0 ] BUCKETIZE 'gts' STORE
[ $gts 1.0 1024.0 1024.0 * 1024.0 * / mapper.mul 0 0 0 ] MAP
}
Vous trouverez l’ensemble dans ce Gist. Et voici le résultat :
Pimp my dashboard
Bon, les goûts et les couleurs… On va skiner un peut tout ça.
D’abord, on peut utiliser un des thèmes de couleurs proposé par la lib, il y en a quelques uns. Choisissons ‘CHARTANA’ au pif.
{
'title' 'Raspi-1'
'options' { 'scheme' 'CHARTANA' 'autoRefresh' 30 }
'tiles' [ ... ]
}
Ensuite, on peut utiliser quelques variables CSS :
<head>
<title>Raspi-1 dashboard</title>
<style>
* {
box-sizing: border-box;
}
:root {
--wc-split-gutter-color : #404040;
--warp-view-pagination-bg-color : #343a40 !important;
--warp-view-pagination-border-color: #6c757d;
--warp-view-datagrid-odd-bg-color : rgba(255, 255, 255, .05);
--warp-view-datagrid-odd-color : #FFFFFF;
--warp-view-datagrid-even-bg-color : #212529;
--warp-view-datagrid-even-color : #FFFFFF;
--warp-view-font-color : #FFFFFF;
--warp-view-chart-label-color : #FFFFFF;
--gts-stack-font-color : #FFFFFF;
--warp-view-resize-handle-color : #111111;
--warp-view-chart-legend-bg : #000;
--gts-labelvalue-font-color : #ccc;
--gts-separator-font-color : #FFFFFF;
--gts-labelname-font-color : rgb(105, 223, 184);
--gts-classname-font-color : rgb(126, 189, 245);
--warp-view-chart-legend-color : #FFFFFF;
--wc-tab-header-color : #FFFFFF;
--wc-tab-header-selected-color : #404040;
--warp-view-tile-background : #3A3C46;
}
body {
font-size : 12px;
line-height : 1.52;
background-color: #333540;
color : #FFFFFF;
padding : 1rem;
height : calc(100vh - 2rem);
}
discovery-dashboard {
color: transparent;
}
</style>
</head>
Et voilà :
On peut aller beaucoup plus loin, on peut par exemple changer la couleur de fond d’une tuile en fonction d’une valeur :
RAND 100.0 * ROUND 'value' STORE // ce qu'on surveille, ici du random entre 0 et 100
{
'type' 'display' 'unit' '%25' // %
'w' 1 'h' 1 'x' 0 'y' 1
'data' {
'data' $value
'globalParams' {
'bgColor'
<% $value 33 < %> <% '#77BE69' %> // value < 33 ? -> vert
<% $value 66 < %> <% '#FF9830' %> // value < 66 ? -> orange
<% '#F24865' %> 2 SWITCH // sinon rouge
'fontColor' 'white' // affichage de 'value' en blanc
}
}
Bref, faites vous plaisir 🙂