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 };
126
127 extraGlobalConfig = mkOption {
128 default = "";
129 description = ''
130 <filename>munin.conf</filename> extra global configuration.
131 See <link xlink:href='http://munin-monitoring.org/wiki/munin.conf' />.
132 Useful to setup notifications, see
133 <link xlink:href='http://munin-monitoring.org/wiki/HowToContact' />
134 '';
135 example = ''
136 contact.email.command mail -s "Munin notification for ''${var:host}" someone@example.com
137 '';
138 };
139
140 hosts = mkOption {
141 example = ''
142 [''${config.networking.hostName}]
143 address localhost
144 '';
145 description = ''
146 Definitions of hosts of nodes to collect data from. Needs at least one
147 hosts for cron to succeed. See
148 <link xlink:href='http://munin-monitoring.org/wiki/munin.conf' />
149 '';
150 };
151
152 };
153
154 };
155
156 config = mkMerge [ (mkIf (nodeCfg.enable || cronCfg.enable) {
157
158 environment.systemPackages = [ pkgs.munin ];
159
160 users.extraUsers = [{
161 name = "munin";
162 description = "Munin monitoring user";
163 group = "munin";
164 uid = config.ids.uids.munin;
165 }];
166
167 users.extraGroups = [{
168 name = "munin";
169 gid = config.ids.gids.munin;
170 }];
171
172 }) (mkIf nodeCfg.enable {
173
174 systemd.services.munin-node = {
175 description = "Munin Node";
176 after = [ "network.target" ];
177 wantedBy = [ "multi-user.target" ];
178 path = [ pkgs.munin ];
179 environment.MUNIN_PLUGSTATE = "/var/run/munin";
180 preStart = ''
181 echo "updating munin plugins..."
182
183 mkdir -p /etc/munin/plugins
184 rm -rf /etc/munin/plugins/*
185 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
186 '';
187 serviceConfig = {
188 ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/";
189 };
190 };
191
192 }) (mkIf cronCfg.enable {
193
194 services.cron.systemCronJobs = [
195 "*/5 * * * * munin ${pkgs.munin}/bin/munin-cron --config ${muninConf}"
196 ];
197
198 system.activationScripts.munin-cron = stringAfter [ "users" "groups" ] ''
199 mkdir -p /var/{run,log,www,lib}/munin
200 chown -R munin:munin /var/{run,log,www,lib}/munin
201 '';
202
203 })];
204}