1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
# Grafana dashboards, built into a form file-provisioning can actually use.
#
# Returns an attrset of { name = derivation; }, where each derivation is a single
# dashboard JSON file. modules/server.nix drops these into /etc/grafana-dashboards.
{ lib, runCommand, fetchurl, jq, dsUid }:
let
# Dashboards exported from grafana.com carry an `__inputs` block and reference their
# datasource as "''${DS_PROMETHEUS}". That placeholder is only substituted by Grafana's
# interactive *import* flow -- file provisioning never runs it, so the uid stays
# literal and every panel fails to resolve a datasource. Substitute at build time.
#
# A dashboard-level `datasource`-type template variable (lowercase, e.g.
# "''${ds_prometheus}") is a different mechanism and resolves fine on its own; this
# only touches names declared in __inputs.
fixupProgram = ''
def dsuid($pluginId):
if $pluginId == "prometheus" then $prom
elif $pluginId == "loki" then $loki
else "" end;
def subst($name; $uid):
walk(if type == "string" then gsub("\\$\\{" + $name + "\\}"; $uid) else . end);
reduce (.__inputs // [])[] as $i (.; subst($i.name; dsuid($i.pluginId)))
| del(.__inputs, .__requires)
# Provisioned dashboards must not carry an id, or Grafana rejects them on collision
# with whatever already occupies that id in its database.
| .id = null
'';
fixup = name: src: runCommand "grafana-dashboard-${name}.json"
{ nativeBuildInputs = [ jq ]; }
''
jq --arg prom ${lib.escapeShellArg dsUid.prometheus} \
--arg loki ${lib.escapeShellArg dsUid.loki} \
${lib.escapeShellArg fixupProgram} < ${src} > "$out"
'';
fromGrafanaCom = { name, id, rev, hash }:
fixup name (fetchurl {
url = "https://grafana.com/api/dashboards/${toString id}/revisions/${toString rev}/download";
inherit hash;
});
in
{
# https://grafana.com/grafana/dashboards/1860
node-exporter-full = fromGrafanaCom {
name = "node-exporter-full";
id = 1860;
rev = 45;
hash = "sha256-GExrdAnzBtp1Ul13cvcZRbEM6iOtFrXXjEaY6g6lGYY=";
};
# https://grafana.com/grafana/dashboards/22604
smartctl-exporter = fromGrafanaCom {
name = "smartctl-exporter";
id = 22604;
rev = 2;
hash = "sha256-ci8WE23fZ+ltEKFoUdNNVXsUIV0jqtas79ia2lYIo88=";
};
# Dashboard 15362 ("ZFS Pool Metrics") used to fill this slot. It is an *InfluxDB*
# dashboard -- its queries are InfluxQL against a `zpool_stats` measurement -- so it
# can never render against Prometheus, however it is provisioned. This is a
# Prometheus-native replacement built on the metrics we actually export: node
# exporter's zfs collector plus the ZFS textfile collector.
# Generated by dashboards/gen_zfs.py.
zfs-pools = fixup "zfs-pools" ../dashboards/zfs-pools.json;
# CrystalDiskInfo-style per-drive SMART view. Generated by dashboards/gen_smart.py.
# The Good/Caution/Bad verdict and the per-drive row titles both come from the
# recording rules in rules/smartctl.yml, so this one needs those loaded to be useful.
drive-health = fixup "drive-health" ../dashboards/smart.json;
}
|