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