1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.moosefs; 7 8 mfsUser = if cfg.runAsUser then "moosefs" else "root"; 9 10 settingsFormat = let 11 listSep = " "; 12 allowedTypes = with types; [ bool int float str ]; 13 valueToString = val: 14 if isList val then concatStringsSep listSep (map (x: valueToString x) val) 15 else if isBool val then (if val then "1" else "0") 16 else toString val; 17 18 in { 19 type = with types; let 20 valueType = oneOf ([ 21 (listOf valueType) 22 ] ++ allowedTypes) // { 23 description = "Flat key-value file"; 24 }; 25 in attrsOf valueType; 26 27 generate = name: value: 28 pkgs.writeText name ( lib.concatStringsSep "\n" ( 29 lib.mapAttrsToList (key: val: "${key} = ${valueToString val}") value )); 30 }; 31 32 33 initTool = pkgs.writeShellScriptBin "mfsmaster-init" '' 34 if [ ! -e ${cfg.master.settings.DATA_PATH}/metadata.mfs ]; then 35 cp ${pkgs.moosefs}/var/mfs/metadata.mfs.empty ${cfg.master.settings.DATA_PATH} 36 chmod +w ${cfg.master.settings.DATA_PATH}/metadata.mfs.empty 37 ${pkgs.moosefs}/bin/mfsmaster -a -c ${masterCfg} start 38 ${pkgs.moosefs}/bin/mfsmaster -c ${masterCfg} stop 39 rm ${cfg.master.settings.DATA_PATH}/metadata.mfs.empty 40 fi 41 ''; 42 43 # master config file 44 masterCfg = settingsFormat.generate 45 "mfsmaster.cfg" cfg.master.settings; 46 47 # metalogger config file 48 metaloggerCfg = settingsFormat.generate 49 "mfsmetalogger.cfg" cfg.metalogger.settings; 50 51 # chunkserver config file 52 chunkserverCfg = settingsFormat.generate 53 "mfschunkserver.cfg" cfg.chunkserver.settings; 54 55 # generic template for all daemons 56 systemdService = name: extraConfig: configFile: { 57 wantedBy = [ "multi-user.target" ]; 58 wants = [ "network-online.target" ]; 59 after = [ "network.target" "network-online.target" ]; 60 61 serviceConfig = { 62 Type = "forking"; 63 ExecStart = "${pkgs.moosefs}/bin/mfs${name} -c ${configFile} start"; 64 ExecStop = "${pkgs.moosefs}/bin/mfs${name} -c ${configFile} stop"; 65 ExecReload = "${pkgs.moosefs}/bin/mfs${name} -c ${configFile} reload"; 66 PIDFile = "${cfg."${name}".settings.DATA_PATH}/.mfs${name}.lock"; 67 } // extraConfig; 68 }; 69 70in { 71 ###### interface 72 73 options = { 74 services.moosefs = { 75 masterHost = mkOption { 76 type = types.str; 77 default = null; 78 description = lib.mdDoc "IP or DNS name of master host."; 79 }; 80 81 runAsUser = mkOption { 82 type = types.bool; 83 default = true; 84 example = true; 85 description = lib.mdDoc "Run daemons as user moosefs instead of root."; 86 }; 87 88 client.enable = mkEnableOption (lib.mdDoc "Moosefs client"); 89 90 master = { 91 enable = mkOption { 92 type = types.bool; 93 description = lib.mdDoc '' 94 Enable Moosefs master daemon. 95 96 You need to run `mfsmaster-init` on a freshly installed master server to 97 initialize the `DATA_PATH` directory. 98 ''; 99 default = false; 100 }; 101 102 exports = mkOption { 103 type = with types; listOf str; 104 default = null; 105 description = lib.mdDoc "Paths to export (see mfsexports.cfg)."; 106 example = [ 107 "* / rw,alldirs,admin,maproot=0:0" 108 "* . rw" 109 ]; 110 }; 111 112 openFirewall = mkOption { 113 type = types.bool; 114 description = lib.mdDoc "Whether to automatically open the necessary ports in the firewall."; 115 default = false; 116 }; 117 118 settings = mkOption { 119 type = types.submodule { 120 freeformType = settingsFormat.type; 121 122 options.DATA_PATH = mkOption { 123 type = types.str; 124 default = "/var/lib/mfs"; 125 description = lib.mdDoc "Data storage directory."; 126 }; 127 }; 128 129 description = lib.mdDoc "Contents of config file (mfsmaster.cfg)."; 130 }; 131 }; 132 133 metalogger = { 134 enable = mkEnableOption (lib.mdDoc "Moosefs metalogger daemon"); 135 136 settings = mkOption { 137 type = types.submodule { 138 freeformType = settingsFormat.type; 139 140 options.DATA_PATH = mkOption { 141 type = types.str; 142 default = "/var/lib/mfs"; 143 description = lib.mdDoc "Data storage directory"; 144 }; 145 }; 146 147 description = lib.mdDoc "Contents of metalogger config file (mfsmetalogger.cfg)."; 148 }; 149 }; 150 151 chunkserver = { 152 enable = mkEnableOption (lib.mdDoc "Moosefs chunkserver daemon"); 153 154 openFirewall = mkOption { 155 type = types.bool; 156 description = lib.mdDoc "Whether to automatically open the necessary ports in the firewall."; 157 default = false; 158 }; 159 160 hdds = mkOption { 161 type = with types; listOf str; 162 default = null; 163 description = lib.mdDoc "Mount points to be used by chunkserver for storage (see mfshdd.cfg)."; 164 example = [ "/mnt/hdd1" ]; 165 }; 166 167 settings = mkOption { 168 type = types.submodule { 169 freeformType = settingsFormat.type; 170 171 options.DATA_PATH = mkOption { 172 type = types.str; 173 default = "/var/lib/mfs"; 174 description = lib.mdDoc "Directory for lock file."; 175 }; 176 }; 177 178 description = lib.mdDoc "Contents of chunkserver config file (mfschunkserver.cfg)."; 179 }; 180 }; 181 }; 182 }; 183 184 ###### implementation 185 186 config = mkIf ( cfg.client.enable || cfg.master.enable || cfg.metalogger.enable || cfg.chunkserver.enable ) { 187 188 warnings = [ ( mkIf (!cfg.runAsUser) "Running moosefs services as root is not recommended.") ]; 189 190 # Service settings 191 services.moosefs = { 192 master.settings = mkIf cfg.master.enable { 193 WORKING_USER = mfsUser; 194 EXPORTS_FILENAME = toString ( pkgs.writeText "mfsexports.cfg" 195 (concatStringsSep "\n" cfg.master.exports)); 196 }; 197 198 metalogger.settings = mkIf cfg.metalogger.enable { 199 WORKING_USER = mfsUser; 200 MASTER_HOST = cfg.masterHost; 201 }; 202 203 chunkserver.settings = mkIf cfg.chunkserver.enable { 204 WORKING_USER = mfsUser; 205 MASTER_HOST = cfg.masterHost; 206 HDD_CONF_FILENAME = toString ( pkgs.writeText "mfshdd.cfg" 207 (concatStringsSep "\n" cfg.chunkserver.hdds)); 208 }; 209 }; 210 211 # Create system user account for daemons 212 users = mkIf ( cfg.runAsUser && ( cfg.master.enable || cfg.metalogger.enable || cfg.chunkserver.enable ) ) { 213 users.moosefs = { 214 isSystemUser = true; 215 description = "moosefs daemon user"; 216 group = "moosefs"; 217 }; 218 groups.moosefs = {}; 219 }; 220 221 environment.systemPackages = 222 (lib.optional cfg.client.enable pkgs.moosefs) ++ 223 (lib.optional cfg.master.enable initTool); 224 225 networking.firewall.allowedTCPPorts = 226 (lib.optionals cfg.master.openFirewall [ 9419 9420 9421 ]) ++ 227 (lib.optional cfg.chunkserver.openFirewall 9422); 228 229 # Ensure storage directories exist 230 systemd.tmpfiles.rules = 231 optional cfg.master.enable "d ${cfg.master.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser}" 232 ++ optional cfg.metalogger.enable "d ${cfg.metalogger.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser}" 233 ++ optional cfg.chunkserver.enable "d ${cfg.chunkserver.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser}"; 234 235 # Service definitions 236 systemd.services.mfs-master = mkIf cfg.master.enable 237 ( systemdService "master" { 238 TimeoutStartSec = 1800; 239 TimeoutStopSec = 1800; 240 Restart = "no"; 241 } masterCfg ); 242 243 systemd.services.mfs-metalogger = mkIf cfg.metalogger.enable 244 ( systemdService "metalogger" { Restart = "on-abnormal"; } metaloggerCfg ); 245 246 systemd.services.mfs-chunkserver = mkIf cfg.chunkserver.enable 247 ( systemdService "chunkserver" { Restart = "on-abnormal"; } chunkserverCfg ); 248 }; 249}