1{ config, pkgs, lib, ... }:
2
3with lib;
4
5let
6 cfg = config.services.collectd;
7
8 baseDirLine = ''BaseDir "${cfg.dataDir}"'';
9 unvalidated_conf = pkgs.writeText "collectd-unvalidated.conf" cfg.extraConfig;
10
11 conf = if cfg.validateConfig then
12 pkgs.runCommand "collectd.conf" {} ''
13 echo testing ${unvalidated_conf}
14 cp ${unvalidated_conf} collectd.conf
15 # collectd -t fails if BaseDir does not exist.
16 substituteInPlace collectd.conf --replace ${lib.escapeShellArgs [ baseDirLine ]} 'BaseDir "."'
17 ${package}/bin/collectd -t -C collectd.conf
18 cp ${unvalidated_conf} $out
19 '' else unvalidated_conf;
20
21 package =
22 if cfg.buildMinimalPackage
23 then minimalPackage
24 else cfg.package;
25
26 minimalPackage = cfg.package.override {
27 enabledPlugins = [ "syslog" ] ++ builtins.attrNames cfg.plugins;
28 };
29
30in {
31 options.services.collectd = with types; {
32 enable = mkEnableOption "collectd agent";
33
34 validateConfig = mkOption {
35 default = true;
36 description = ''
37 Validate the syntax of collectd configuration file at build time.
38 Disable this if you use the Include directive on files unavailable in
39 the build sandbox, or when cross-compiling.
40 '';
41 type = types.bool;
42 };
43
44 package = mkPackageOption pkgs "collectd" { };
45
46 buildMinimalPackage = mkOption {
47 default = false;
48 description = ''
49 Build a minimal collectd package with only the configured `services.collectd.plugins`
50 '';
51 type = bool;
52 };
53
54 user = mkOption {
55 default = "collectd";
56 description = ''
57 User under which to run collectd.
58 '';
59 type = nullOr str;
60 };
61
62 dataDir = mkOption {
63 default = "/var/lib/collectd";
64 description = ''
65 Data directory for collectd agent.
66 '';
67 type = path;
68 };
69
70 autoLoadPlugin = mkOption {
71 default = false;
72 description = ''
73 Enable plugin autoloading.
74 '';
75 type = bool;
76 };
77
78 include = mkOption {
79 default = [];
80 description = ''
81 Additional paths to load config from.
82 '';
83 type = listOf str;
84 };
85
86 plugins = mkOption {
87 default = {};
88 example = { cpu = ""; memory = ""; network = "Server 192.168.1.1 25826"; };
89 description = ''
90 Attribute set of plugin names to plugin config segments
91 '';
92 type = attrsOf lines;
93 };
94
95 extraConfig = mkOption {
96 default = "";
97 description = ''
98 Extra configuration for collectd. Use mkBefore to add lines before the
99 default config, and mkAfter to add them below.
100 '';
101 type = lines;
102 };
103
104 };
105
106 config = mkIf cfg.enable {
107 # 1200 is after the default (1000) but before mkAfter (1500).
108 services.collectd.extraConfig = lib.mkOrder 1200 ''
109 ${baseDirLine}
110 AutoLoadPlugin ${boolToString cfg.autoLoadPlugin}
111 Hostname "${config.networking.hostName}"
112
113 LoadPlugin syslog
114 <Plugin "syslog">
115 LogLevel "info"
116 NotifyLevel "OKAY"
117 </Plugin>
118
119 ${concatStrings (mapAttrsToList (plugin: pluginConfig: ''
120 LoadPlugin ${plugin}
121 <Plugin "${plugin}">
122 ${pluginConfig}
123 </Plugin>
124 '') cfg.plugins)}
125
126 ${concatMapStrings (f: ''
127 Include "${f}"
128 '') cfg.include}
129 '';
130
131 systemd.tmpfiles.rules = [
132 "d '${cfg.dataDir}' - ${cfg.user} - - -"
133 ];
134
135 systemd.services.collectd = {
136 description = "Collectd Monitoring Agent";
137 after = [ "network.target" ];
138 wantedBy = [ "multi-user.target" ];
139
140 serviceConfig = {
141 ExecStart = "${package}/sbin/collectd -C ${conf} -f";
142 User = cfg.user;
143 Restart = "on-failure";
144 RestartSec = 3;
145 };
146 };
147
148 users.users = optionalAttrs (cfg.user == "collectd") {
149 collectd = {
150 isSystemUser = true;
151 group = "collectd";
152 };
153 };
154
155 users.groups = optionalAttrs (cfg.user == "collectd") {
156 collectd = {};
157 };
158 };
159}