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