grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / computing / slurm / slurm.nix
blobf4944d3ce31863cb230090338b42a2189e0a92c8
1 { config, lib, options, pkgs, ... }:
2 let
4   cfg = config.services.slurm;
5   opt = options.services.slurm;
6   # configuration file can be generated by https://slurm.schedmd.com/configurator.html
8   defaultUser = "slurm";
10   configFile = pkgs.writeTextDir "slurm.conf"
11     ''
12       ClusterName=${cfg.clusterName}
13       StateSaveLocation=${cfg.stateSaveLocation}
14       SlurmUser=${cfg.user}
15       ${lib.optionalString (cfg.controlMachine != null) "controlMachine=${cfg.controlMachine}"}
16       ${lib.optionalString (cfg.controlAddr != null) "controlAddr=${cfg.controlAddr}"}
17       ${toString (map (x: "NodeName=${x}\n") cfg.nodeName)}
18       ${toString (map (x: "PartitionName=${x}\n") cfg.partitionName)}
19       PlugStackConfig=${plugStackConfig}/plugstack.conf
20       ProctrackType=${cfg.procTrackType}
21       ${cfg.extraConfig}
22     '';
24   plugStackConfig = pkgs.writeTextDir "plugstack.conf"
25     ''
26       ${lib.optionalString cfg.enableSrunX11 "optional ${pkgs.slurm-spank-x11}/lib/x11.so"}
27       ${cfg.extraPlugstackConfig}
28     '';
30   cgroupConfig = pkgs.writeTextDir "cgroup.conf"
31    ''
32      ${cfg.extraCgroupConfig}
33    '';
35   slurmdbdConf = pkgs.writeText "slurmdbd.conf"
36    ''
37      DbdHost=${cfg.dbdserver.dbdHost}
38      SlurmUser=${cfg.user}
39      StorageType=accounting_storage/mysql
40      StorageUser=${cfg.dbdserver.storageUser}
41      ${cfg.dbdserver.extraConfig}
42    '';
44   # slurm expects some additional config files to be
45   # in the same directory as slurm.conf
46   etcSlurm = pkgs.symlinkJoin {
47     name = "etc-slurm";
48     paths = [ configFile cgroupConfig plugStackConfig ] ++ cfg.extraConfigPaths;
49   };
54   ###### interface
56   meta.maintainers = [ lib.maintainers.markuskowa ];
58   options = {
60     services.slurm = {
62       server = {
63         enable = lib.mkOption {
64           type = lib.types.bool;
65           default = false;
66           description = ''
67             Whether to enable the slurm control daemon.
68             Note that the standard authentication method is "munge".
69             The "munge" service needs to be provided with a password file in order for
70             slurm to work properly (see `services.munge.password`).
71           '';
72         };
73       };
75       dbdserver = {
76         enable = lib.mkEnableOption "SlurmDBD service";
78         dbdHost = lib.mkOption {
79           type = lib.types.str;
80           default = config.networking.hostName;
81           defaultText = lib.literalExpression "config.networking.hostName";
82           description = ''
83             Hostname of the machine where `slurmdbd`
84             is running (i.e. name returned by `hostname -s`).
85           '';
86         };
88         storageUser = lib.mkOption {
89           type = lib.types.str;
90           default = cfg.user;
91           defaultText = lib.literalExpression "config.${opt.user}";
92           description = ''
93             Database user name.
94           '';
95         };
97         storagePassFile = lib.mkOption {
98           type = with lib.types; nullOr str;
99           default = null;
100           description = ''
101             Path to file with database password. The content of this will be used to
102             create the password for the `StoragePass` option.
103           '';
104         };
106         extraConfig = lib.mkOption {
107           type = lib.types.lines;
108           default = "";
109           description = ''
110             Extra configuration for `slurmdbd.conf` See also:
111             {manpage}`slurmdbd.conf(8)`.
112           '';
113         };
114       };
116       client = {
117         enable = lib.mkEnableOption "slurm client daemon";
118       };
120       enableStools = lib.mkOption {
121         type = lib.types.bool;
122         default = false;
123         description = ''
124           Whether to provide a slurm.conf file.
125           Enable this option if you do not run a slurm daemon on this host
126           (i.e. `server.enable` and `client.enable` are `false`)
127           but you still want to run slurm commands from this host.
128         '';
129       };
131       package = lib.mkPackageOption pkgs "slurm" {
132         example = "slurm-full";
133       } // {
134         default = pkgs.slurm.override { enableX11 = ! cfg.enableSrunX11; };
135       };
137       controlMachine = lib.mkOption {
138         type = lib.types.nullOr lib.types.str;
139         default = null;
140         example = null;
141         description = ''
142           The short hostname of the machine where SLURM control functions are
143           executed (i.e. the name returned by the command "hostname -s", use "tux001"
144           rather than "tux001.my.com").
145         '';
146       };
148       controlAddr = lib.mkOption {
149         type = lib.types.nullOr lib.types.str;
150         default = cfg.controlMachine;
151         defaultText = lib.literalExpression "config.${opt.controlMachine}";
152         example = null;
153         description = ''
154           Name that ControlMachine should be referred to in establishing a
155           communications path.
156         '';
157       };
159       clusterName = lib.mkOption {
160         type = lib.types.str;
161         default = "default";
162         example = "myCluster";
163         description = ''
164           Necessary to distinguish accounting records in a multi-cluster environment.
165         '';
166       };
168       nodeName = lib.mkOption {
169         type = lib.types.listOf lib.types.str;
170         default = [];
171         example = lib.literalExpression ''[ "linux[1-32] CPUs=1 State=UNKNOWN" ];'';
172         description = ''
173           Name that SLURM uses to refer to a node (or base partition for BlueGene
174           systems). Typically this would be the string that "/bin/hostname -s"
175           returns. Note that now you have to write node's parameters after the name.
176         '';
177       };
179       partitionName = lib.mkOption {
180         type = lib.types.listOf lib.types.str;
181         default = [];
182         example = lib.literalExpression ''[ "debug Nodes=linux[1-32] Default=YES MaxTime=INFINITE State=UP" ];'';
183         description = ''
184           Name by which the partition may be referenced. Note that now you have
185           to write the partition's parameters after the name.
186         '';
187       };
189       enableSrunX11 = lib.mkOption {
190         default = false;
191         type = lib.types.bool;
192         description = ''
193           If enabled srun will accept the option "--x11" to allow for X11 forwarding
194           from within an interactive session or a batch job. This activates the
195           slurm-spank-x11 module. Note that this option also enables
196           {option}`services.openssh.forwardX11` on the client.
198           This option requires slurm to be compiled without native X11 support.
199           The default behavior is to re-compile the slurm package with native X11
200           support disabled if this option is set to true.
202           To use the native X11 support add `PrologFlags=X11` in {option}`extraConfig`.
203           Note that this method will only work RSA SSH host keys.
204         '';
205       };
207       procTrackType = lib.mkOption {
208         type = lib.types.str;
209         default = "proctrack/linuxproc";
210         description = ''
211           Plugin to be used for process tracking on a job step basis.
212           The slurmd daemon uses this mechanism to identify all processes
213           which are children of processes it spawns for a user job step.
214         '';
215       };
217       stateSaveLocation = lib.mkOption {
218         type = lib.types.str;
219         default = "/var/spool/slurmctld";
220         description = ''
221           Directory into which the Slurm controller, slurmctld, saves its state.
222         '';
223       };
225       user = lib.mkOption {
226         type = lib.types.str;
227         default = defaultUser;
228         description = ''
229           Set this option when you want to run the slurmctld daemon
230           as something else than the default slurm user "slurm".
231           Note that the UID of this user needs to be the same
232           on all nodes.
233         '';
234       };
236       extraConfig = lib.mkOption {
237         default = "";
238         type = lib.types.lines;
239         description = ''
240           Extra configuration options that will be added verbatim at
241           the end of the slurm configuration file.
242         '';
243       };
245       extraPlugstackConfig = lib.mkOption {
246         default = "";
247         type = lib.types.lines;
248         description = ''
249           Extra configuration that will be added to the end of `plugstack.conf`.
250         '';
251       };
253       extraCgroupConfig = lib.mkOption {
254         default = "";
255         type = lib.types.lines;
256         description = ''
257           Extra configuration for `cgroup.conf`. This file is
258           used when `procTrackType=proctrack/cgroup`.
259         '';
260       };
262       extraConfigPaths = lib.mkOption {
263         type = with lib.types; listOf path;
264         default = [];
265         description = ''
266           Slurm expects config files for plugins in the same path
267           as `slurm.conf`. Add extra nix store
268           paths that should be merged into same directory as
269           `slurm.conf`.
270         '';
271       };
273       etcSlurm = lib.mkOption {
274         type = lib.types.path;
275         internal = true;
276         default = etcSlurm;
277         defaultText = lib.literalMD ''
278           Directory created from generated config files and
279           `config.${opt.extraConfigPaths}`.
280         '';
281         description = ''
282           Path to directory with slurm config files. This option is set by default from the
283           Slurm module and is meant to make the Slurm config file available to other modules.
284         '';
285       };
287     };
289   };
291   imports = [
292     (lib.mkRemovedOptionModule [ "services" "slurm" "dbdserver" "storagePass" ] ''
293       This option has been removed so that the database password is not exposed via the nix store.
294       Use services.slurm.dbdserver.storagePassFile to provide the database password.
295     '')
296     (lib.mkRemovedOptionModule [ "services" "slurm" "dbdserver" "configFile" ] ''
297       This option has been removed. Use services.slurm.dbdserver.storagePassFile
298       and services.slurm.dbdserver.extraConfig instead.
299     '')
300   ];
302   ###### implementation
304   config =
305     let
306       wrappedSlurm = pkgs.stdenv.mkDerivation {
307         name = "wrappedSlurm";
309         builder = pkgs.writeText "builder.sh" ''
310           source $stdenv/setup
311           mkdir -p $out/bin
312           find  ${lib.getBin cfg.package}/bin -type f -executable | while read EXE
313           do
314             exename="$(basename $EXE)"
315             wrappername="$out/bin/$exename"
316             cat > "$wrappername" <<EOT
317           #!/bin/sh
318           if [ -z "$SLURM_CONF" ]
319           then
320             SLURM_CONF="${cfg.etcSlurm}/slurm.conf" "$EXE" "\$@"
321           else
322             "$EXE" "\$0"
323           fi
324           EOT
325             chmod +x "$wrappername"
326           done
328           mkdir -p $out/share
329           ln -s ${lib.getBin cfg.package}/share/man $out/share/man
330         '';
331       };
333   in lib.mkIf ( cfg.enableStools ||
334             cfg.client.enable ||
335             cfg.server.enable ||
336             cfg.dbdserver.enable ) {
338     environment.systemPackages = [ wrappedSlurm ];
340     services.munge.enable = lib.mkDefault true;
342     # use a static uid as default to ensure it is the same on all nodes
343     users.users.slurm = lib.mkIf (cfg.user == defaultUser) {
344       name = defaultUser;
345       group = "slurm";
346       uid = config.ids.uids.slurm;
347     };
349     users.groups.slurm.gid = config.ids.uids.slurm;
351     systemd.services.slurmd = lib.mkIf (cfg.client.enable) {
352       path = with pkgs; [ wrappedSlurm coreutils ]
353         ++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
355       wantedBy = [ "multi-user.target" ];
356       after = [
357         "systemd-tmpfiles-clean.service"
358         "munge.service"
359         "network-online.target"
360         "remote-fs.target"
361       ];
362       wants = [ "network-online.target" ];
364       serviceConfig = {
365         Type = "forking";
366         KillMode = "process";
367         ExecStart = "${wrappedSlurm}/bin/slurmd";
368         PIDFile = "/run/slurmd.pid";
369         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
370         LimitMEMLOCK = "infinity";
371         Delegate="Yes";
372       };
373     };
375     systemd.tmpfiles.rules = lib.mkIf cfg.client.enable [
376       "d /var/spool/slurmd 755 root root -"
377     ];
379     services.openssh.settings.X11Forwarding = lib.mkIf cfg.client.enable (lib.mkDefault true);
381     systemd.services.slurmctld = lib.mkIf (cfg.server.enable) {
382       path = with pkgs; [ wrappedSlurm munge coreutils ]
383         ++ lib.optional cfg.enableSrunX11 slurm-spank-x11;
385       wantedBy = [ "multi-user.target" ];
386       after = [ "network.target" "munged.service" ];
387       requires = [ "munged.service" ];
389       serviceConfig = {
390         Type = "forking";
391         ExecStart = "${wrappedSlurm}/bin/slurmctld";
392         PIDFile = "/run/slurmctld.pid";
393         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
394       };
396       preStart = ''
397         mkdir -p ${cfg.stateSaveLocation}
398         chown -R ${cfg.user}:slurm ${cfg.stateSaveLocation}
399       '';
400     };
402     systemd.services.slurmdbd = let
403       # slurm strips the last component off the path
404       configPath = "$RUNTIME_DIRECTORY/slurmdbd.conf";
405     in lib.mkIf (cfg.dbdserver.enable) {
406       path = with pkgs; [ wrappedSlurm munge coreutils ];
408       wantedBy = [ "multi-user.target" ];
409       after = [ "network.target" "munged.service" "mysql.service" ];
410       requires = [ "munged.service" "mysql.service" ];
412       preStart = ''
413         install -m 600 -o ${cfg.user} -T ${slurmdbdConf} ${configPath}
414         ${lib.optionalString (cfg.dbdserver.storagePassFile != null) ''
415           echo "StoragePass=$(cat ${cfg.dbdserver.storagePassFile})" \
416             >> ${configPath}
417         ''}
418       '';
420       script = ''
421         export SLURM_CONF=${configPath}
422         exec ${cfg.package}/bin/slurmdbd -D
423       '';
425       serviceConfig = {
426         RuntimeDirectory = "slurmdbd";
427         Type = "simple";
428         PIDFile = "/run/slurmdbd.pid";
429         ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
430       };
431     };
433   };