1{ config, lib, pkgs, ... }:
2
3# TODO: support munin-async
4# TODO: LWP/Pg perl libs aren't recognized
5
6# TODO: support fastcgi
7# http://munin-monitoring.org/wiki/CgiHowto2
8# spawn-fcgi -s /var/run/munin/fastcgi-graph.sock -U www-data -u munin -g munin /usr/lib/munin/cgi/munin-cgi-graph
9# spawn-fcgi -s /var/run/munin/fastcgi-html.sock -U www-data -u munin -g munin /usr/lib/munin/cgi/munin-cgi-html
10# https://paste.sh/vofcctHP#-KbDSXVeWoifYncZmLfZzgum
11# nginx http://munin.readthedocs.org/en/latest/example/webserver/nginx.html
12
13
14with lib;
15
16let
17 nodeCfg = config.services.munin-node;
18 cronCfg = config.services.munin-cron;
19
20 muninPlugins = pkgs.stdenv.mkDerivation {
21 name = "munin-available-plugins";
22 buildCommand = ''
23 mkdir -p $out
24
25 cp --preserve=mode ${pkgs.munin}/lib/plugins/* $out/
26
27 for file in $out/*; do
28 case "$file" in
29 */plugin.sh|*/plugins.history)
30 chmod +x "$file"
31 continue;;
32 esac
33
34 # read magic makers from the file
35 family=$(sed -nr 's/.*#%#\s+family\s*=\s*(\S+)\s*/\1/p' $file)
36 cap=$(sed -nr 's/.*#%#\s+capabilities\s*=\s*(.+)/\1/p' $file)
37
38 wrapProgram $file \
39 --set PATH "/run/wrappers/bin:/run/current-system/sw/bin" \
40 --set MUNIN_LIBDIR "${pkgs.munin}/lib" \
41 --set MUNIN_PLUGSTATE "/var/run/munin"
42
43 # munin uses markers to tell munin-node-configure what a plugin can do
44 echo "#%# family=$family" >> $file
45 echo "#%# capabilities=$cap" >> $file
46 done
47
48 # NOTE: we disable disktstats because plugin seems to fail and it hangs html generation (100% CPU + memory leak)
49 rm -f $out/diskstats
50 '';
51 buildInputs = [ pkgs.makeWrapper ];
52 };
53
54 muninConf = pkgs.writeText "munin.conf"
55 ''
56 dbdir /var/lib/munin
57 htmldir /var/www/munin
58 logdir /var/log/munin
59 rundir /var/run/munin
60
61 ${cronCfg.extraGlobalConfig}
62
63 ${cronCfg.hosts}
64 '';
65
66 nodeConf = pkgs.writeText "munin-node.conf"
67 ''
68 log_level 3
69 log_file Sys::Syslog
70 port 4949
71 host *
72 background 0
73 user root
74 group root
75 host_name ${config.networking.hostName}
76 setsid 0
77
78 # wrapped plugins by makeWrapper being with dots
79 ignore_file ^\.
80
81 allow ^::1$
82 allow ^127\.0\.0\.1$
83
84 ${nodeCfg.extraConfig}
85 '';
86in
87
88{
89
90 options = {
91
92 services.munin-node = {
93
94 enable = mkOption {
95 default = false;
96 description = ''
97 Enable Munin Node agent. Munin node listens on 0.0.0.0 and
98 by default accepts connections only from 127.0.0.1 for security reasons.
99
100 See <link xlink:href='http://munin-monitoring.org/wiki/munin-node.conf' />.
101 '';
102 };
103
104 extraConfig = mkOption {
105 default = "";
106 type = types.lines;
107 description = ''
108 <filename>munin-node.conf</filename> extra configuration. See
109 <link xlink:href='http://munin-monitoring.org/wiki/munin-node.conf' />
110 '';
111 };
112
113 # TODO: add option to add additional plugins
114
115 };
116
117 services.munin-cron = {
118
119 enable = mkOption {
120 default = false;
121 description = ''
122 Enable munin-cron. Takes care of all heavy lifting to collect data from
123 nodes and draws graphs to html. Runs munin-update, munin-limits,
124 munin-graphs and munin-html in that order.
125
126 HTML output is in <filename>/var/www/munin/</filename>, configure your
127 favourite webserver to serve static files.
128 '';
129 };
130
131 extraGlobalConfig = mkOption {
132 default = "";
133 description = ''
134 <filename>munin.conf</filename> extra global configuration.
135 See <link xlink:href='http://munin-monitoring.org/wiki/munin.conf' />.
136 Useful to setup notifications, see
137 <link xlink:href='http://munin-monitoring.org/wiki/HowToContact' />
138 '';
139 example = ''
140 contact.email.command mail -s "Munin notification for ''${var:host}" someone@example.com
141 '';
142 };
143
144 hosts = mkOption {
145 example = ''
146 [''${config.networking.hostName}]
147 address localhost
148 '';
149 description = ''
150 Definitions of hosts of nodes to collect data from. Needs at least one
151 hosts for cron to succeed. See
152 <link xlink:href='http://munin-monitoring.org/wiki/munin.conf' />
153 '';
154 };
155
156 };
157
158 };
159
160 config = mkMerge [ (mkIf (nodeCfg.enable || cronCfg.enable) {
161
162 environment.systemPackages = [ pkgs.munin ];
163
164 users.extraUsers = [{
165 name = "munin";
166 description = "Munin monitoring user";
167 group = "munin";
168 uid = config.ids.uids.munin;
169 }];
170
171 users.extraGroups = [{
172 name = "munin";
173 gid = config.ids.gids.munin;
174 }];
175
176 }) (mkIf nodeCfg.enable {
177
178 systemd.services.munin-node = {
179 description = "Munin Node";
180 after = [ "network.target" ];
181 wantedBy = [ "multi-user.target" ];
182 path = [ pkgs.munin ];
183 environment.MUNIN_PLUGSTATE = "/var/run/munin";
184 preStart = ''
185 echo "updating munin plugins..."
186
187 mkdir -p /etc/munin/plugins
188 rm -rf /etc/munin/plugins/*
189 PATH="/run/wrappers/bin:/run/current-system/sw/bin" ${pkgs.munin}/sbin/munin-node-configure --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${muninPlugins} --servicedir=/etc/munin/plugins 2>/dev/null | ${pkgs.bash}/bin/bash
190 '';
191 serviceConfig = {
192 ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/";
193 };
194 };
195
196 }) (mkIf cronCfg.enable {
197
198 systemd.timers.munin-cron = {
199 description = "batch Munin master programs";
200 wantedBy = [ "timers.target" ];
201 timerConfig.OnCalendar = "*:0/5";
202 };
203
204 systemd.services.munin-cron = {
205 description = "batch Munin master programs";
206 unitConfig.Documentation = "man:munin-cron(8)";
207
208 serviceConfig = {
209 Type = "oneshot";
210 User = "munin";
211 ExecStart = "${pkgs.munin}/bin/munin-cron --config ${muninConf}";
212 };
213 };
214
215 system.activationScripts.munin-cron = stringAfter [ "users" "groups" ] ''
216 mkdir -p /var/{run,log,www,lib}/munin
217 chown -R munin:munin /var/{run,log,www,lib}/munin
218 '';
219 })];
220}