1{ config, lib, pkgs, ... }:
2
3# TODO: test configuration when building nixexpr (use -t parameter)
4# TODO: support sqlite3 (it's deprecate?) and mysql
5
6with lib;
7
8let
9 libDir = "/var/lib/bacula";
10
11 fd_cfg = config.services.bacula-fd;
12 fd_conf = pkgs.writeText "bacula-fd.conf"
13 ''
14 Client {
15 Name = "${fd_cfg.name}";
16 FDPort = ${toString fd_cfg.port};
17 WorkingDirectory = "${libDir}";
18 Pid Directory = "/var/run";
19 ${fd_cfg.extraClientConfig}
20 }
21
22 ${concatStringsSep "\n" (mapAttrsToList (name: value: ''
23 Director {
24 Name = "${name}";
25 Password = "${value.password}";
26 Monitor = "${value.monitor}";
27 }
28 '') fd_cfg.director)}
29
30 Messages {
31 Name = Standard;
32 syslog = all, !skipped, !restored
33 ${fd_cfg.extraMessagesConfig}
34 }
35 '';
36
37 sd_cfg = config.services.bacula-sd;
38 sd_conf = pkgs.writeText "bacula-sd.conf"
39 ''
40 Storage {
41 Name = "${sd_cfg.name}";
42 SDPort = ${toString sd_cfg.port};
43 WorkingDirectory = "${libDir}";
44 Pid Directory = "/var/run";
45 ${sd_cfg.extraStorageConfig}
46 }
47
48 ${concatStringsSep "\n" (mapAttrsToList (name: value: ''
49 Device {
50 Name = "${name}";
51 Archive Device = "${value.archiveDevice}";
52 Media Type = "${value.mediaType}";
53 ${value.extraDeviceConfig}
54 }
55 '') sd_cfg.device)}
56
57 ${concatStringsSep "\n" (mapAttrsToList (name: value: ''
58 Director {
59 Name = "${name}";
60 Password = "${value.password}";
61 Monitor = "${value.monitor}";
62 }
63 '') sd_cfg.director)}
64
65 Messages {
66 Name = Standard;
67 syslog = all, !skipped, !restored
68 ${sd_cfg.extraMessagesConfig}
69 }
70 '';
71
72 dir_cfg = config.services.bacula-dir;
73 dir_conf = pkgs.writeText "bacula-dir.conf"
74 ''
75 Director {
76 Name = "${dir_cfg.name}";
77 Password = "${dir_cfg.password}";
78 DirPort = ${toString dir_cfg.port};
79 Working Directory = "${libDir}";
80 Pid Directory = "/var/run/";
81 QueryFile = "${pkgs.bacula}/etc/query.sql";
82 ${dir_cfg.extraDirectorConfig}
83 }
84
85 Catalog {
86 Name = "PostgreSQL";
87 dbname = "bacula";
88 user = "bacula";
89 }
90
91 Messages {
92 Name = Standard;
93 syslog = all, !skipped, !restored
94 ${dir_cfg.extraMessagesConfig}
95 }
96
97 ${dir_cfg.extraConfig}
98 '';
99
100 # TODO: by default use this config
101 bconsole_conf = pkgs.writeText "bconsole.conf"
102 ''
103 Director {
104 Name = ${dir_cfg.name};
105 Address = "localhost";
106 DirPort = ${toString dir_cfg.port};
107 Password = "${dir_cfg.password}";
108 }
109 '';
110
111 directorOptions = {name, config, ...}:
112 {
113 options = {
114 password = mkOption {
115 # TODO: required?
116 description = ''
117 Specifies the password that must be supplied for a Director to b
118 '';
119 };
120
121 monitor = mkOption {
122 default = "no";
123 example = "yes";
124 description = ''
125 If Monitor is set to no (default), this director will have full
126 '';
127 };
128 };
129 };
130
131 deviceOptions = {name, config, ...}:
132 {
133 options = {
134 archiveDevice = mkOption {
135 # TODO: required?
136 description = ''
137 The specified name-string gives the system file name of the storage device managed by this storage daemon. This will usually be the device file name of a removable storage device (tape drive), for example " /dev/nst0" or "/dev/rmt/0mbn". For a DVD-writer, it will be for example /dev/hdc. It may also be a directory name if you are archiving to disk storage.
138 '';
139 };
140
141 mediaType = mkOption {
142 # TODO: required?
143 description = ''
144 The specified name-string names the type of media supported by this device, for example, "DLT7000". Media type names are arbitrary in that you set them to anything you want, but they must be known to the volume database to keep track of which storage daemons can read which volumes. In general, each different storage type should have a unique Media Type associated with it. The same name-string must appear in the appropriate Storage resource definition in the Director's configuration file.
145 '';
146 };
147
148 extraDeviceConfig = mkOption {
149 default = "";
150 description = ''
151 Extra configuration to be passed in Device directive.
152 '';
153 example = ''
154 LabelMedia = yes
155 Random Access = no
156 AutomaticMount = no
157 RemovableMedia = no
158 MaximumOpenWait = 60
159 AlwaysOpen = no
160 '';
161 };
162 };
163 };
164
165in {
166 options = {
167 services.bacula-fd = {
168 enable = mkOption {
169 type = types.bool;
170 default = false;
171 description = ''
172 Whether to enable the Bacula File Daemon.
173 '';
174 };
175
176 name = mkOption {
177 default = "${config.networking.hostName}-fd";
178 description = ''
179 The client name that must be used by the Director when connecting.
180 Generally, it is a good idea to use a name related to the machine
181 so that error messages can be easily identified if you have multiple
182 Clients. This directive is required.
183 '';
184 };
185
186 port = mkOption {
187 default = 9102;
188 type = types.int;
189 description = ''
190 This specifies the port number on which the Client listens for
191 Director connections. It must agree with the FDPort specified in
192 the Client resource of the Director's configuration file.
193 '';
194 };
195
196 director = mkOption {
197 default = {};
198 description = ''
199 This option defines director resources in Bacula File Daemon.
200 '';
201 type = types.attrsOf types.optionSet;
202 options = [ directorOptions ];
203 };
204
205 extraClientConfig = mkOption {
206 default = "";
207 description = ''
208 Extra configuration to be passed in Client directive.
209 '';
210 example = literalExample ''
211 Maximum Concurrent Jobs = 20;
212 Heartbeat Interval = 30;
213 '';
214 };
215
216 extraMessagesConfig = mkOption {
217 default = "";
218 description = ''
219 Extra configuration to be passed in Messages directive.
220 '';
221 example = literalExample ''
222 console = all
223 '';
224 };
225 };
226
227 services.bacula-sd = {
228 enable = mkOption {
229 type = types.bool;
230 default = false;
231 description = ''
232 Whether to enable Bacula Storage Daemon.
233 '';
234 };
235
236 name = mkOption {
237 default = "${config.networking.hostName}-sd";
238 description = ''
239 Specifies the Name of the Storage daemon.
240 '';
241 };
242
243 port = mkOption {
244 default = 9103;
245 type = types.int;
246 description = ''
247 Specifies port number on which the Storage daemon listens for Director connections. The default is 9103.
248 '';
249 };
250
251 director = mkOption {
252 default = {};
253 description = ''
254 This option defines Director resources in Bacula Storage Daemon.
255 '';
256 type = types.attrsOf types.optionSet;
257 options = [ directorOptions ];
258 };
259
260 device = mkOption {
261 default = {};
262 description = ''
263 This option defines Device resources in Bacula Storage Daemon.
264 '';
265 type = types.attrsOf types.optionSet;
266 options = [ deviceOptions ];
267 };
268
269 extraStorageConfig = mkOption {
270 default = "";
271 description = ''
272 Extra configuration to be passed in Storage directive.
273 '';
274 example = ''
275 Maximum Concurrent Jobs = 20;
276 Heartbeat Interval = 30;
277 '';
278 };
279
280 extraMessagesConfig = mkOption {
281 default = "";
282 description = ''
283 Extra configuration to be passed in Messages directive.
284 '';
285 example = ''
286 console = all
287 '';
288 };
289
290 };
291
292 services.bacula-dir = {
293 enable = mkOption {
294 type = types.bool;
295 default = false;
296 description = ''
297 Whether to enable Bacula Director Daemon.
298 '';
299 };
300
301 name = mkOption {
302 default = "${config.networking.hostName}-dir";
303 description = ''
304 The director name used by the system administrator. This directive is required.
305 '';
306 };
307
308 port = mkOption {
309 default = 9101;
310 type = types.int;
311 description = ''
312 Specify the port (a positive integer) on which the Director daemon will listen for Bacula Console connections. This same port number must be specified in the Director resource of the Console configuration file. The default is 9101, so normally this directive need not be specified. This directive should not be used if you specify DirAddresses (N.B plural) directive.
313 '';
314 };
315
316 password = mkOption {
317 # TODO: required?
318 description = ''
319 Specifies the password that must be supplied for a Director.
320 '';
321 };
322
323 extraMessagesConfig = mkOption {
324 default = "";
325 description = ''
326 Extra configuration to be passed in Messages directive.
327 '';
328 example = ''
329 console = all
330 '';
331 };
332
333 extraDirectorConfig = mkOption {
334 default = "";
335 description = ''
336 Extra configuration to be passed in Director directive.
337 '';
338 example = ''
339 Maximum Concurrent Jobs = 20;
340 Heartbeat Interval = 30;
341 '';
342 };
343
344 extraConfig = mkOption {
345 default = "";
346 description = ''
347 Extra configuration for Bacula Director Daemon.
348 '';
349 example = ''
350 TODO
351 '';
352 };
353 };
354 };
355
356 config = mkIf (fd_cfg.enable || sd_cfg.enable || dir_cfg.enable) {
357 systemd.services.bacula-fd = mkIf fd_cfg.enable {
358 after = [ "network.target" ];
359 description = "Bacula File Daemon";
360 wantedBy = [ "multi-user.target" ];
361 path = [ pkgs.bacula ];
362 serviceConfig.ExecStart = "${pkgs.bacula}/sbin/bacula-fd -f -u root -g bacula -c ${fd_conf}";
363 serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
364 };
365
366 systemd.services.bacula-sd = mkIf sd_cfg.enable {
367 after = [ "network.target" ];
368 description = "Bacula Storage Daemon";
369 wantedBy = [ "multi-user.target" ];
370 path = [ pkgs.bacula ];
371 serviceConfig.ExecStart = "${pkgs.bacula}/sbin/bacula-sd -f -u bacula -g bacula -c ${sd_conf}";
372 serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
373 };
374
375 services.postgresql.enable = dir_cfg.enable == true;
376
377 systemd.services.bacula-dir = mkIf dir_cfg.enable {
378 after = [ "network.target" "postgresql.service" ];
379 description = "Bacula Director Daemon";
380 wantedBy = [ "multi-user.target" ];
381 path = [ pkgs.bacula ];
382 serviceConfig.ExecStart = "${pkgs.bacula}/sbin/bacula-dir -f -u bacula -g bacula -c ${dir_conf}";
383 serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
384 preStart = ''
385 if ! test -e "${libDir}/db-created"; then
386 ${pkgs.postgresql}/bin/createuser --no-superuser --no-createdb --no-createrole bacula
387 #${pkgs.postgresql}/bin/createdb --owner bacula bacula
388
389 # populate DB
390 ${pkgs.bacula}/etc/create_bacula_database postgresql
391 ${pkgs.bacula}/etc/make_bacula_tables postgresql
392 ${pkgs.bacula}/etc/grant_bacula_privileges postgresql
393 touch "${libDir}/db-created"
394 else
395 ${pkgs.bacula}/etc/update_bacula_tables postgresql || true
396 fi
397 '';
398 };
399
400 environment.systemPackages = [ pkgs.bacula ];
401
402 users.extraUsers.bacula = {
403 group = "bacula";
404 uid = config.ids.uids.bacula;
405 home = "${libDir}";
406 createHome = true;
407 description = "Bacula Daemons user";
408 shell = "${pkgs.bash}/bin/bash";
409 };
410
411 users.extraGroups.bacula.gid = config.ids.gids.bacula;
412 };
413}