1{ config, lib, pkgs, ... }:
2
3with lib;
4let
5
6 cfg = config.services.smokeping;
7 smokepingHome = "/var/lib/smokeping";
8 smokepingPidDir = "/run";
9 configFile =
10 if cfg.config == null
11 then
12 ''
13 *** General ***
14 cgiurl = ${cfg.cgiUrl}
15 contact = ${cfg.ownerEmail}
16 datadir = ${smokepingHome}/data
17 imgcache = ${smokepingHome}/cache
18 imgurl = ${cfg.imgUrl}
19 linkstyle = ${cfg.linkStyle}
20 ${lib.optionalString (cfg.mailHost != "") "mailhost = ${cfg.mailHost}"}
21 owner = ${cfg.owner}
22 pagedir = ${smokepingHome}/cache
23 piddir = ${smokepingPidDir}
24 ${lib.optionalString (cfg.sendmail != null) "sendmail = ${cfg.sendmail}"}
25 smokemail = ${cfg.smokeMailTemplate}
26 *** Presentation ***
27 template = ${cfg.presentationTemplate}
28 ${cfg.presentationConfig}
29 *** Alerts ***
30 ${cfg.alertConfig}
31 *** Database ***
32 ${cfg.databaseConfig}
33 *** Probes ***
34 ${cfg.probeConfig}
35 *** Targets ***
36 ${cfg.targetConfig}
37 ${cfg.extraConfig}
38 ''
39 else
40 cfg.config;
41
42 configPath = pkgs.writeText "smokeping.conf" configFile;
43 cgiHome = pkgs.writeScript "smokeping.fcgi" ''
44 #!${pkgs.bash}/bin/bash
45 ${cfg.package}/bin/smokeping_cgi ${configPath}
46 '';
47in
48
49{
50 options = {
51 services.smokeping = {
52 enable = mkOption {
53 type = types.bool;
54 default = false;
55 description = "Enable the smokeping service";
56 };
57 alertConfig = mkOption {
58 type = types.lines;
59 default = ''
60 to = root@localhost
61 from = smokeping@localhost
62 '';
63 example = literalExample ''
64 to = alertee@address.somewhere
65 from = smokealert@company.xy
66
67 +someloss
68 type = loss
69 # in percent
70 pattern = >0%,*12*,>0%,*12*,>0%
71 comment = loss 3 times in a row;
72 '';
73 description = "Configuration for alerts.";
74 };
75 cgiUrl = mkOption {
76 type = types.str;
77 default = "http://${cfg.hostName}:${toString cfg.port}/smokeping.cgi";
78 defaultText = "http://\${hostName}:\${toString port}/smokeping.cgi";
79 example = "https://somewhere.example.com/smokeping.cgi";
80 description = "URL to the smokeping cgi.";
81 };
82 config = mkOption {
83 type = types.nullOr types.lines;
84 default = null;
85 description = "Full smokeping config supplied by the user. Overrides " +
86 "and replaces any other configuration supplied.";
87 };
88 databaseConfig = mkOption {
89 type = types.lines;
90 default = ''
91 step = 300
92 pings = 20
93 # consfn mrhb steps total
94 AVERAGE 0.5 1 1008
95 AVERAGE 0.5 12 4320
96 MIN 0.5 12 4320
97 MAX 0.5 12 4320
98 AVERAGE 0.5 144 720
99 MAX 0.5 144 720
100 MIN 0.5 144 720
101
102 '';
103 example = literalExample ''
104 # near constant pings.
105 step = 30
106 pings = 20
107 # consfn mrhb steps total
108 AVERAGE 0.5 1 10080
109 AVERAGE 0.5 12 43200
110 MIN 0.5 12 43200
111 MAX 0.5 12 43200
112 AVERAGE 0.5 144 7200
113 MAX 0.5 144 7200
114 MIN 0.5 144 7200
115 '';
116 description = ''Configure the ping frequency and retention of the rrd files.
117 Once set, changing the interval will require deletion or migration of all
118 the collected data.'';
119 };
120 extraConfig = mkOption {
121 type = types.lines;
122 default = "";
123 description = "Any additional customization not already included.";
124 };
125 hostName = mkOption {
126 type = types.str;
127 default = config.networking.fqdn;
128 defaultText = "\${config.networking.fqdn}";
129 example = "somewhere.example.com";
130 description = "DNS name for the urls generated in the cgi.";
131 };
132 imgUrl = mkOption {
133 type = types.str;
134 default = "http://${cfg.hostName}:${toString cfg.port}/cache";
135 defaultText = "http://\${hostName}:\${toString port}/cache";
136 example = "https://somewhere.example.com/cache";
137 description = "Base url for images generated in the cgi.";
138 };
139 linkStyle = mkOption {
140 type = types.enum ["original" "absolute" "relative"];
141 default = "relative";
142 example = "absolute";
143 description = "DNS name for the urls generated in the cgi.";
144 };
145 mailHost = mkOption {
146 type = types.str;
147 default = "";
148 example = "localhost";
149 description = "Use this SMTP server to send alerts";
150 };
151 owner = mkOption {
152 type = types.str;
153 default = "nobody";
154 example = "Joe Admin";
155 description = "Real name of the owner of the instance";
156 };
157 ownerEmail = mkOption {
158 type = types.str;
159 default = "no-reply@${cfg.hostName}";
160 defaultText = "no-reply@\${hostName}";
161 example = "no-reply@yourdomain.com";
162 description = "Email contact for owner";
163 };
164 package = mkOption {
165 type = types.package;
166 default = pkgs.smokeping;
167 defaultText = "pkgs.smokeping";
168 description = "Specify a custom smokeping package";
169 };
170 port = mkOption {
171 type = types.int;
172 default = 8081;
173 example = 8081;
174 description = "TCP port to use for the web server.";
175 };
176 presentationConfig = mkOption {
177 type = types.lines;
178 default = ''
179 + charts
180 menu = Charts
181 title = The most interesting destinations
182 ++ stddev
183 sorter = StdDev(entries=>4)
184 title = Top Standard Deviation
185 menu = Std Deviation
186 format = Standard Deviation %f
187 ++ max
188 sorter = Max(entries=>5)
189 title = Top Max Roundtrip Time
190 menu = by Max
191 format = Max Roundtrip Time %f seconds
192 ++ loss
193 sorter = Loss(entries=>5)
194 title = Top Packet Loss
195 menu = Loss
196 format = Packets Lost %f
197 ++ median
198 sorter = Median(entries=>5)
199 title = Top Median Roundtrip Time
200 menu = by Median
201 format = Median RTT %f seconds
202 + overview
203 width = 600
204 height = 50
205 range = 10h
206 + detail
207 width = 600
208 height = 200
209 unison_tolerance = 2
210 "Last 3 Hours" 3h
211 "Last 30 Hours" 30h
212 "Last 10 Days" 10d
213 "Last 360 Days" 360d
214 '';
215 description = "presentation graph style";
216 };
217 presentationTemplate = mkOption {
218 type = types.str;
219 default = "${pkgs.smokeping}/etc/basepage.html.dist";
220 description = "Default page layout for the web UI.";
221 };
222 probeConfig = mkOption {
223 type = types.lines;
224 default = ''
225 + FPing
226 binary = ${config.security.wrapperDir}/fping
227 '';
228 description = "Probe configuration";
229 };
230 sendmail = mkOption {
231 type = types.nullOr types.path;
232 default = null;
233 example = "/run/wrappers/bin/sendmail";
234 description = "Use this sendmail compatible script to deliver alerts";
235 };
236 smokeMailTemplate = mkOption {
237 type = types.str;
238 default = "${cfg.package}/etc/smokemail.dist";
239 description = "Specify the smokemail template for alerts.";
240 };
241 targetConfig = mkOption {
242 type = types.lines;
243 default = ''
244 probe = FPing
245 menu = Top
246 title = Network Latency Grapher
247 remark = Welcome to the SmokePing website of xxx Company. \
248 Here you will learn all about the latency of our network.
249 + Local
250 menu = Local
251 title = Local Network
252 ++ LocalMachine
253 menu = Local Machine
254 title = This host
255 host = localhost
256 '';
257 description = "Target configuration";
258 };
259 user = mkOption {
260 type = types.str;
261 default = "smokeping";
262 description = "User that runs smokeping and (optionally) thttpd";
263 };
264 webService = mkOption {
265 type = types.bool;
266 default = true;
267 description = "Enable a smokeping web interface";
268 };
269 };
270
271 };
272
273 config = mkIf cfg.enable {
274 assertions = [
275 {
276 assertion = !(cfg.sendmail != null && cfg.mailHost != "");
277 message = "services.smokeping: sendmail and Mailhost cannot both be enabled.";
278 }
279 ];
280 security.wrappers = {
281 fping.source = "${pkgs.fping}/bin/fping";
282 fping6.source = "${pkgs.fping}/bin/fping6";
283 };
284 environment.systemPackages = [ pkgs.fping ];
285 users.users.${cfg.user} = {
286 isNormalUser = false;
287 isSystemUser = true;
288 uid = config.ids.uids.smokeping;
289 description = "smokeping daemon user";
290 home = smokepingHome;
291 createHome = true;
292 };
293 systemd.services.smokeping = {
294 wantedBy = [ "multi-user.target"];
295 serviceConfig = {
296 User = cfg.user;
297 Restart = "on-failure";
298 };
299 preStart = ''
300 mkdir -m 0755 -p ${smokepingHome}/cache ${smokepingHome}/data
301 rm -f ${smokepingHome}/cropper
302 ln -s ${cfg.package}/htdocs/cropper ${smokepingHome}/cropper
303 rm -f ${smokepingHome}/smokeping.fcgi
304 ln -s ${cgiHome} ${smokepingHome}/smokeping.fcgi
305 ${cfg.package}/bin/smokeping --check --config=${configPath}
306 ${cfg.package}/bin/smokeping --static --config=${configPath}
307 '';
308 script = "${cfg.package}/bin/smokeping --config=${configPath} --nodaemon";
309 };
310 systemd.services.thttpd = mkIf cfg.webService {
311 wantedBy = [ "multi-user.target"];
312 requires = [ "smokeping.service"];
313 partOf = [ "smokeping.service"];
314 path = with pkgs; [ bash rrdtool smokeping thttpd ];
315 script = ''thttpd -u ${cfg.user} -c "**.fcgi" -d ${smokepingHome} -p ${builtins.toString cfg.port} -D -nos'';
316 serviceConfig.Restart = "always";
317 };
318 };
319
320 meta.maintainers = with lib.maintainers; [ erictapen ];
321}
322