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 (lib.mdDoc "collectd agent");
33
34 validateConfig = mkOption {
35 default = true;
36 description = lib.mdDoc ''
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 = mkOption {
45 default = pkgs.collectd;
46 defaultText = literalExpression "pkgs.collectd";
47 description = lib.mdDoc ''
48 Which collectd package to use.
49 '';
50 type = types.package;
51 };
52
53 buildMinimalPackage = mkOption {
54 default = false;
55 description = lib.mdDoc ''
56 Build a minimal collectd package with only the configured `services.collectd.plugins`
57 '';
58 type = bool;
59 };
60
61 user = mkOption {
62 default = "collectd";
63 description = lib.mdDoc ''
64 User under which to run collectd.
65 '';
66 type = nullOr str;
67 };
68
69 dataDir = mkOption {
70 default = "/var/lib/collectd";
71 description = lib.mdDoc ''
72 Data directory for collectd agent.
73 '';
74 type = path;
75 };
76
77 autoLoadPlugin = mkOption {
78 default = false;
79 description = lib.mdDoc ''
80 Enable plugin autoloading.
81 '';
82 type = bool;
83 };
84
85 include = mkOption {
86 default = [];
87 description = lib.mdDoc ''
88 Additional paths to load config from.
89 '';
90 type = listOf str;
91 };
92
93 plugins = mkOption {
94 default = {};
95 example = { cpu = ""; memory = ""; network = "Server 192.168.1.1 25826"; };
96 description = lib.mdDoc ''
97 Attribute set of plugin names to plugin config segments
98 '';
99 type = attrsOf lines;
100 };
101
102 extraConfig = mkOption {
103 default = "";
104 description = lib.mdDoc ''
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 = 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 ${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 ${concatStrings (mapAttrsToList (plugin: pluginConfig: ''
127 LoadPlugin ${plugin}
128 <Plugin "${plugin}">
129 ${pluginConfig}
130 </Plugin>
131 '') cfg.plugins)}
132
133 ${concatMapStrings (f: ''
134 Include "${f}"
135 '') cfg.include}
136 '';
137
138 systemd.tmpfiles.rules = [
139 "d '${cfg.dataDir}' - ${cfg.user} - - -"
140 ];
141
142 systemd.services.collectd = {
143 description = "Collectd Monitoring Agent";
144 after = [ "network.target" ];
145 wantedBy = [ "multi-user.target" ];
146
147 serviceConfig = {
148 ExecStart = "${package}/sbin/collectd -C ${conf} -f";
149 User = cfg.user;
150 Restart = "on-failure";
151 RestartSec = 3;
152 };
153 };
154
155 users.users = optionalAttrs (cfg.user == "collectd") {
156 collectd = {
157 isSystemUser = true;
158 group = "collectd";
159 };
160 };
161
162 users.groups = optionalAttrs (cfg.user == "collectd") {
163 collectd = {};
164 };
165 };
166}