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