at 21.11-pre 13 kB view raw
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://guide.munin-monitoring.org/en/latest/example/webserver/apache-cgi.html 8# spawn-fcgi -s /run/munin/fastcgi-graph.sock -U www-data -u munin -g munin /usr/lib/munin/cgi/munin-cgi-graph 9# spawn-fcgi -s /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 https://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 muninConf = pkgs.writeText "munin.conf" 21 '' 22 dbdir /var/lib/munin 23 htmldir /var/www/munin 24 logdir /var/log/munin 25 rundir /run/munin 26 27 ${lib.optionalString (cronCfg.extraCSS != "") "staticdir ${customStaticDir}"} 28 29 ${cronCfg.extraGlobalConfig} 30 31 ${cronCfg.hosts} 32 ''; 33 34 nodeConf = pkgs.writeText "munin-node.conf" 35 '' 36 log_level 3 37 log_file Sys::Syslog 38 port 4949 39 host * 40 background 0 41 user root 42 group root 43 host_name ${config.networking.hostName} 44 setsid 0 45 46 # wrapped plugins by makeWrapper being with dots 47 ignore_file ^\. 48 49 allow ^::1$ 50 allow ^127\.0\.0\.1$ 51 52 ${nodeCfg.extraConfig} 53 ''; 54 55 pluginConf = pkgs.writeText "munin-plugin-conf" 56 '' 57 [hddtemp_smartctl] 58 user root 59 group root 60 61 [meminfo] 62 user root 63 group root 64 65 [ipmi*] 66 user root 67 group root 68 69 [munin*] 70 env.UPDATE_STATSFILE /var/lib/munin/munin-update.stats 71 72 ${nodeCfg.extraPluginConfig} 73 ''; 74 75 pluginConfDir = pkgs.stdenv.mkDerivation { 76 name = "munin-plugin-conf.d"; 77 buildCommand = '' 78 mkdir $out 79 ln -s ${pluginConf} $out/nixos-config 80 ''; 81 }; 82 83 # Copy one Munin plugin into the Nix store with a specific name. 84 # This is suitable for use with plugins going directly into /etc/munin/plugins, 85 # i.e. munin.extraPlugins. 86 internOnePlugin = name: path: 87 "cp -a '${path}' '${name}'"; 88 89 # Copy an entire tree of Munin plugins into a single directory in the Nix 90 # store, with no renaming. 91 # This is suitable for use with munin-node-configure --suggest, i.e. 92 # munin.extraAutoPlugins. 93 internManyPlugins = name: path: 94 "find '${path}' -type f -perm /a+x -exec cp -a -t . '{}' '+'"; 95 96 # Use the appropriate intern-fn to copy the plugins into the store and patch 97 # them afterwards in an attempt to get them to run on NixOS. 98 internAndFixPlugins = name: intern-fn: paths: 99 pkgs.runCommand name {} '' 100 mkdir -p "$out" 101 cd "$out" 102 ${lib.concatStringsSep "\n" 103 (lib.attrsets.mapAttrsToList intern-fn paths)} 104 chmod -R u+w . 105 find . -type f -exec sed -E -i ' 106 s,(/usr)?/s?bin/,/run/current-system/sw/bin/,g 107 ' '{}' '+' 108 ''; 109 110 # TODO: write a derivation for munin-contrib, so that for contrib plugins 111 # you can just refer to them by name rather than needing to include a copy 112 # of munin-contrib in your nixos configuration. 113 extraPluginDir = internAndFixPlugins "munin-extra-plugins.d" 114 internOnePlugin nodeCfg.extraPlugins; 115 116 extraAutoPluginDir = internAndFixPlugins "munin-extra-auto-plugins.d" 117 internManyPlugins 118 (builtins.listToAttrs 119 (map 120 (path: { name = baseNameOf path; value = path; }) 121 nodeCfg.extraAutoPlugins)); 122 123 customStaticDir = pkgs.runCommand "munin-custom-static-data" {} '' 124 cp -a "${pkgs.munin}/etc/opt/munin/static" "$out" 125 cd "$out" 126 chmod -R u+w . 127 echo "${cronCfg.extraCSS}" >> style.css 128 echo "${cronCfg.extraCSS}" >> style-new.css 129 ''; 130in 131 132{ 133 134 options = { 135 136 services.munin-node = { 137 138 enable = mkOption { 139 default = false; 140 type = types.bool; 141 description = '' 142 Enable Munin Node agent. Munin node listens on 0.0.0.0 and 143 by default accepts connections only from 127.0.0.1 for security reasons. 144 145 See <link xlink:href='http://guide.munin-monitoring.org/en/latest/architecture/index.html' />. 146 ''; 147 }; 148 149 extraConfig = mkOption { 150 default = ""; 151 type = types.lines; 152 description = '' 153 <filename>munin-node.conf</filename> extra configuration. See 154 <link xlink:href='http://guide.munin-monitoring.org/en/latest/reference/munin-node.conf.html' /> 155 ''; 156 }; 157 158 extraPluginConfig = mkOption { 159 default = ""; 160 type = types.lines; 161 description = '' 162 <filename>plugin-conf.d</filename> extra plugin configuration. See 163 <link xlink:href='http://guide.munin-monitoring.org/en/latest/plugin/use.html' /> 164 ''; 165 example = '' 166 [fail2ban_*] 167 user root 168 ''; 169 }; 170 171 extraPlugins = mkOption { 172 default = {}; 173 type = with types; attrsOf path; 174 description = '' 175 Additional Munin plugins to activate. Keys are the name of the plugin 176 symlink, values are the path to the underlying plugin script. You 177 can use the same plugin script multiple times (e.g. for wildcard 178 plugins). 179 180 Note that these plugins do not participate in autoconfiguration. If 181 you want to autoconfigure additional plugins, use 182 <option>services.munin-node.extraAutoPlugins</option>. 183 184 Plugins enabled in this manner take precedence over autoconfigured 185 plugins. 186 187 Plugins will be copied into the Nix store, and it will attempt to 188 modify them to run properly by fixing hardcoded references to 189 <literal>/bin</literal>, <literal>/usr/bin</literal>, 190 <literal>/sbin</literal>, and <literal>/usr/sbin</literal>. 191 ''; 192 example = literalExample '' 193 { 194 zfs_usage_bigpool = /src/munin-contrib/plugins/zfs/zfs_usage_; 195 zfs_usage_smallpool = /src/munin-contrib/plugins/zfs/zfs_usage_; 196 zfs_list = /src/munin-contrib/plugins/zfs/zfs_list; 197 }; 198 ''; 199 }; 200 201 extraAutoPlugins = mkOption { 202 default = []; 203 type = with types; listOf path; 204 description = '' 205 Additional Munin plugins to autoconfigure, using 206 <literal>munin-node-configure --suggest</literal>. These should be 207 the actual paths to the plugin files (or directories containing them), 208 not just their names. 209 210 If you want to manually enable individual plugins instead, use 211 <option>services.munin-node.extraPlugins</option>. 212 213 Note that only plugins that have the 'autoconfig' capability will do 214 anything if listed here, since plugins that cannot autoconfigure 215 won't be automatically enabled by 216 <literal>munin-node-configure</literal>. 217 218 Plugins will be copied into the Nix store, and it will attempt to 219 modify them to run properly by fixing hardcoded references to 220 <literal>/bin</literal>, <literal>/usr/bin</literal>, 221 <literal>/sbin</literal>, and <literal>/usr/sbin</literal>. 222 ''; 223 example = literalExample '' 224 [ 225 /src/munin-contrib/plugins/zfs 226 /src/munin-contrib/plugins/ssh 227 ]; 228 ''; 229 }; 230 231 disabledPlugins = mkOption { 232 # TODO: figure out why Munin isn't writing the log file and fix it. 233 # In the meantime this at least suppresses a useless graph full of 234 # NaNs in the output. 235 default = [ "munin_stats" ]; 236 type = with types; listOf str; 237 description = '' 238 Munin plugins to disable, even if 239 <literal>munin-node-configure --suggest</literal> tries to enable 240 them. To disable a wildcard plugin, use an actual wildcard, as in 241 the example. 242 243 munin_stats is disabled by default as it tries to read 244 <literal>/var/log/munin/munin-update.log</literal> for timing 245 information, and the NixOS build of Munin does not write this file. 246 ''; 247 example = [ "diskstats" "zfs_usage_*" ]; 248 }; 249 }; 250 251 services.munin-cron = { 252 253 enable = mkOption { 254 default = false; 255 type = types.bool; 256 description = '' 257 Enable munin-cron. Takes care of all heavy lifting to collect data from 258 nodes and draws graphs to html. Runs munin-update, munin-limits, 259 munin-graphs and munin-html in that order. 260 261 HTML output is in <filename>/var/www/munin/</filename>, configure your 262 favourite webserver to serve static files. 263 ''; 264 }; 265 266 extraGlobalConfig = mkOption { 267 default = ""; 268 type = types.lines; 269 description = '' 270 <filename>munin.conf</filename> extra global configuration. 271 See <link xlink:href='http://guide.munin-monitoring.org/en/latest/reference/munin.conf.html' />. 272 Useful to setup notifications, see 273 <link xlink:href='http://guide.munin-monitoring.org/en/latest/tutorial/alert.html' /> 274 ''; 275 example = '' 276 contact.email.command mail -s "Munin notification for ''${var:host}" someone@example.com 277 ''; 278 }; 279 280 hosts = mkOption { 281 default = ""; 282 type = types.lines; 283 description = '' 284 Definitions of hosts of nodes to collect data from. Needs at least one 285 host for cron to succeed. See 286 <link xlink:href='http://guide.munin-monitoring.org/en/latest/reference/munin.conf.html' /> 287 ''; 288 example = '' 289 [''${config.networking.hostName}] 290 address localhost 291 ''; 292 }; 293 294 extraCSS = mkOption { 295 default = ""; 296 type = types.lines; 297 description = '' 298 Custom styling for the HTML that munin-cron generates. This will be 299 appended to the CSS files used by munin-cron and will thus take 300 precedence over the builtin styles. 301 ''; 302 example = '' 303 /* A simple dark theme. */ 304 html, body { background: #222222; } 305 #header, #footer { background: #333333; } 306 img.i, img.iwarn, img.icrit, img.iunkn { 307 filter: invert(100%) hue-rotate(-30deg); 308 } 309 ''; 310 }; 311 312 }; 313 314 }; 315 316 config = mkMerge [ (mkIf (nodeCfg.enable || cronCfg.enable) { 317 318 environment.systemPackages = [ pkgs.munin ]; 319 320 users.users.munin = { 321 description = "Munin monitoring user"; 322 group = "munin"; 323 uid = config.ids.uids.munin; 324 home = "/var/lib/munin"; 325 }; 326 327 users.groups.munin = { 328 gid = config.ids.gids.munin; 329 }; 330 331 }) (mkIf nodeCfg.enable { 332 333 systemd.services.munin-node = { 334 description = "Munin Node"; 335 after = [ "network.target" ]; 336 wantedBy = [ "multi-user.target" ]; 337 path = with pkgs; [ munin smartmontools "/run/current-system/sw" "/run/wrappers" ]; 338 environment.MUNIN_LIBDIR = "${pkgs.munin}/lib"; 339 environment.MUNIN_PLUGSTATE = "/run/munin"; 340 environment.MUNIN_LOGDIR = "/var/log/munin"; 341 preStart = '' 342 echo "Updating munin plugins..." 343 344 mkdir -p /etc/munin/plugins 345 rm -rf /etc/munin/plugins/* 346 347 # Autoconfigure builtin plugins 348 ${pkgs.munin}/bin/munin-node-configure --suggest --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${pkgs.munin}/lib/plugins --servicedir=/etc/munin/plugins --sconfdir=${pluginConfDir} 2>/dev/null | ${pkgs.bash}/bin/bash 349 350 # Autoconfigure extra plugins 351 ${pkgs.munin}/bin/munin-node-configure --suggest --shell --families contrib,auto,manual --config ${nodeConf} --libdir=${extraAutoPluginDir} --servicedir=/etc/munin/plugins --sconfdir=${pluginConfDir} 2>/dev/null | ${pkgs.bash}/bin/bash 352 353 ${lib.optionalString (nodeCfg.extraPlugins != {}) '' 354 # Link in manually enabled plugins 355 ln -f -s -t /etc/munin/plugins ${extraPluginDir}/* 356 ''} 357 358 ${lib.optionalString (nodeCfg.disabledPlugins != []) '' 359 # Disable plugins 360 cd /etc/munin/plugins 361 rm -f ${toString nodeCfg.disabledPlugins} 362 ''} 363 ''; 364 serviceConfig = { 365 ExecStart = "${pkgs.munin}/sbin/munin-node --config ${nodeConf} --servicedir /etc/munin/plugins/ --sconfdir=${pluginConfDir}"; 366 }; 367 }; 368 369 # munin_stats plugin breaks as of 2.0.33 when this doesn't exist 370 systemd.tmpfiles.rules = [ "d /run/munin 0755 munin munin -" ]; 371 372 }) (mkIf cronCfg.enable { 373 374 # Munin is hardcoded to use DejaVu Mono and the graphs come out wrong if 375 # it's not available. 376 fonts.fonts = [ pkgs.dejavu_fonts ]; 377 378 systemd.timers.munin-cron = { 379 description = "batch Munin master programs"; 380 wantedBy = [ "timers.target" ]; 381 timerConfig.OnCalendar = "*:0/5"; 382 }; 383 384 systemd.services.munin-cron = { 385 description = "batch Munin master programs"; 386 unitConfig.Documentation = "man:munin-cron(8)"; 387 388 serviceConfig = { 389 Type = "oneshot"; 390 User = "munin"; 391 ExecStart = "${pkgs.munin}/bin/munin-cron --config ${muninConf}"; 392 }; 393 }; 394 395 systemd.tmpfiles.rules = [ 396 "d /run/munin 0755 munin munin -" 397 "d /var/log/munin 0755 munin munin -" 398 "d /var/www/munin 0755 munin munin -" 399 "d /var/lib/munin 0755 munin munin -" 400 ]; 401 })]; 402}