{ config, lib, pkgs, ... }: with lib; let cfg = config.services.moosefs; mfsUser = if cfg.runAsUser then "moosefs" else "root"; settingsFormat = let listSep = " "; allowedTypes = with types; [ bool int float str ]; valueToString = val: if isList val then concatStringsSep listSep (map (x: valueToString x) val) else if isBool val then (if val then "1" else "0") else toString val; in { type = with types; let valueType = oneOf ([ (listOf valueType) ] ++ allowedTypes) // { description = "Flat key-value file"; }; in attrsOf valueType; generate = name: value: pkgs.writeText name ( lib.concatStringsSep "\n" ( lib.mapAttrsToList (key: val: "${key} = ${valueToString val}") value )); }; initTool = pkgs.writeShellScriptBin "mfsmaster-init" '' if [ ! -e ${cfg.master.settings.DATA_PATH}/metadata.mfs ]; then cp ${pkgs.moosefs}/var/mfs/metadata.mfs.empty ${cfg.master.settings.DATA_PATH} chmod +w ${cfg.master.settings.DATA_PATH}/metadata.mfs.empty ${pkgs.moosefs}/bin/mfsmaster -a -c ${masterCfg} start ${pkgs.moosefs}/bin/mfsmaster -c ${masterCfg} stop rm ${cfg.master.settings.DATA_PATH}/metadata.mfs.empty fi ''; # master config file masterCfg = settingsFormat.generate "mfsmaster.cfg" cfg.master.settings; # metalogger config file metaloggerCfg = settingsFormat.generate "mfsmetalogger.cfg" cfg.metalogger.settings; # chunkserver config file chunkserverCfg = settingsFormat.generate "mfschunkserver.cfg" cfg.chunkserver.settings; # generic template for all daemons systemdService = name: extraConfig: configFile: { wantedBy = [ "multi-user.target" ]; wants = [ "network-online.target" ]; after = [ "network.target" "network-online.target" ]; serviceConfig = { Type = "forking"; ExecStart = "${pkgs.moosefs}/bin/mfs${name} -c ${configFile} start"; ExecStop = "${pkgs.moosefs}/bin/mfs${name} -c ${configFile} stop"; ExecReload = "${pkgs.moosefs}/bin/mfs${name} -c ${configFile} reload"; PIDFile = "${cfg."${name}".settings.DATA_PATH}/.mfs${name}.lock"; } // extraConfig; }; in { ###### interface options = { services.moosefs = { masterHost = mkOption { type = types.str; default = null; description = "IP or DNS name of master host."; }; runAsUser = mkOption { type = types.bool; default = true; example = true; description = "Run daemons as user moosefs instead of root."; }; client.enable = mkEnableOption "Moosefs client"; master = { enable = mkOption { type = types.bool; description = '' Enable Moosefs master daemon. You need to run `mfsmaster-init` on a freshly installed master server to initialize the `DATA_PATH` directory. ''; default = false; }; exports = mkOption { type = with types; listOf str; default = null; description = "Paths to export (see mfsexports.cfg)."; example = [ "* / rw,alldirs,admin,maproot=0:0" "* . rw" ]; }; openFirewall = mkOption { type = types.bool; description = "Whether to automatically open the necessary ports in the firewall."; default = false; }; settings = mkOption { type = types.submodule { freeformType = settingsFormat.type; options.DATA_PATH = mkOption { type = types.str; default = "/var/lib/mfs"; description = "Data storage directory."; }; }; description = "Contents of config file (mfsmaster.cfg)."; }; }; metalogger = { enable = mkEnableOption "Moosefs metalogger daemon"; settings = mkOption { type = types.submodule { freeformType = settingsFormat.type; options.DATA_PATH = mkOption { type = types.str; default = "/var/lib/mfs"; description = "Data storage directory"; }; }; description = "Contents of metalogger config file (mfsmetalogger.cfg)."; }; }; chunkserver = { enable = mkEnableOption "Moosefs chunkserver daemon"; openFirewall = mkOption { type = types.bool; description = "Whether to automatically open the necessary ports in the firewall."; default = false; }; hdds = mkOption { type = with types; listOf str; default = null; description = "Mount points to be used by chunkserver for storage (see mfshdd.cfg)."; example = [ "/mnt/hdd1" ]; }; settings = mkOption { type = types.submodule { freeformType = settingsFormat.type; options.DATA_PATH = mkOption { type = types.str; default = "/var/lib/mfs"; description = "Directory for lock file."; }; }; description = "Contents of chunkserver config file (mfschunkserver.cfg)."; }; }; }; }; ###### implementation config = mkIf ( cfg.client.enable || cfg.master.enable || cfg.metalogger.enable || cfg.chunkserver.enable ) { warnings = [ ( mkIf (!cfg.runAsUser) "Running moosefs services as root is not recommended.") ]; # Service settings services.moosefs = { master.settings = mkIf cfg.master.enable { WORKING_USER = mfsUser; EXPORTS_FILENAME = toString ( pkgs.writeText "mfsexports.cfg" (concatStringsSep "\n" cfg.master.exports)); }; metalogger.settings = mkIf cfg.metalogger.enable { WORKING_USER = mfsUser; MASTER_HOST = cfg.masterHost; }; chunkserver.settings = mkIf cfg.chunkserver.enable { WORKING_USER = mfsUser; MASTER_HOST = cfg.masterHost; HDD_CONF_FILENAME = toString ( pkgs.writeText "mfshdd.cfg" (concatStringsSep "\n" cfg.chunkserver.hdds)); }; }; # Create system user account for daemons users = mkIf ( cfg.runAsUser && ( cfg.master.enable || cfg.metalogger.enable || cfg.chunkserver.enable ) ) { users.moosefs = { isSystemUser = true; description = "moosefs daemon user"; group = "moosefs"; }; groups.moosefs = {}; }; environment.systemPackages = (lib.optional cfg.client.enable pkgs.moosefs) ++ (lib.optional cfg.master.enable initTool); networking.firewall.allowedTCPPorts = (lib.optionals cfg.master.openFirewall [ 9419 9420 9421 ]) ++ (lib.optional cfg.chunkserver.openFirewall 9422); # Ensure storage directories exist systemd.tmpfiles.rules = optional cfg.master.enable "d ${cfg.master.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser}" ++ optional cfg.metalogger.enable "d ${cfg.metalogger.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser}" ++ optional cfg.chunkserver.enable "d ${cfg.chunkserver.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser}"; # Service definitions systemd.services.mfs-master = mkIf cfg.master.enable ( systemdService "master" { TimeoutStartSec = 1800; TimeoutStopSec = 1800; Restart = "no"; } masterCfg ); systemd.services.mfs-metalogger = mkIf cfg.metalogger.enable ( systemdService "metalogger" { Restart = "on-abnormal"; } metaloggerCfg ); systemd.services.mfs-chunkserver = mkIf cfg.chunkserver.enable ( systemdService "chunkserver" { Restart = "on-abnormal"; } chunkserverCfg ); }; }