1{ config, lib, pkgs, ...} : 2 3with lib; 4 5let 6 cfg = config.services.orangefs.server; 7 8 aliases = mapAttrsToList (alias: url: alias) cfg.servers; 9 10 # Maximum handle number is 2^63 11 maxHandle = 9223372036854775806; 12 13 # One range of handles for each meta/data instance 14 handleStep = maxHandle / (length aliases) / 2; 15 16 fileSystems = mapAttrsToList (name: fs: '' 17 <FileSystem> 18 Name ${name} 19 ID ${toString fs.id} 20 RootHandle ${toString fs.rootHandle} 21 22 ${fs.extraConfig} 23 24 <MetaHandleRanges> 25 ${concatStringsSep "\n" ( 26 imap0 (i: alias: 27 let 28 begin = i * handleStep + 3; 29 end = begin + handleStep - 1; 30 in "Range ${alias} ${toString begin}-${toString end}") aliases 31 )} 32 </MetaHandleRanges> 33 34 <DataHandleRanges> 35 ${concatStringsSep "\n" ( 36 imap0 (i: alias: 37 let 38 begin = i * handleStep + 3 + (length aliases) * handleStep; 39 end = begin + handleStep - 1; 40 in "Range ${alias} ${toString begin}-${toString end}") aliases 41 )} 42 </DataHandleRanges> 43 44 <StorageHints> 45 TroveSyncMeta ${if fs.troveSyncMeta then "yes" else "no"} 46 TroveSyncData ${if fs.troveSyncData then "yes" else "no"} 47 ${fs.extraStorageHints} 48 </StorageHints> 49 50 </FileSystem> 51 '') cfg.fileSystems; 52 53 configFile = '' 54 <Defaults> 55 LogType ${cfg.logType} 56 DataStorageSpace ${cfg.dataStorageSpace} 57 MetaDataStorageSpace ${cfg.metadataStorageSpace} 58 59 BMIModules ${concatStringsSep "," cfg.BMIModules} 60 ${cfg.extraDefaults} 61 </Defaults> 62 63 ${cfg.extraConfig} 64 65 <Aliases> 66 ${concatStringsSep "\n" (mapAttrsToList (alias: url: "Alias ${alias} ${url}") cfg.servers)} 67 </Aliases> 68 69 ${concatStringsSep "\n" fileSystems} 70 ''; 71 72in { 73 ###### interface 74 75 options = { 76 services.orangefs.server = { 77 enable = mkEnableOption (lib.mdDoc "OrangeFS server"); 78 79 logType = mkOption { 80 type = with types; enum [ "file" "syslog" ]; 81 default = "syslog"; 82 description = lib.mdDoc "Destination for log messages."; 83 }; 84 85 dataStorageSpace = mkOption { 86 type = types.nullOr types.str; 87 default = null; 88 example = "/data/storage"; 89 description = lib.mdDoc "Directory for data storage."; 90 }; 91 92 metadataStorageSpace = mkOption { 93 type = types.nullOr types.str; 94 default = null; 95 example = "/data/meta"; 96 description = lib.mdDoc "Directory for meta data storage."; 97 }; 98 99 BMIModules = mkOption { 100 type = with types; listOf str; 101 default = [ "bmi_tcp" ]; 102 example = [ "bmi_tcp" "bmi_ib"]; 103 description = lib.mdDoc "List of BMI modules to load."; 104 }; 105 106 extraDefaults = mkOption { 107 type = types.lines; 108 default = ""; 109 description = lib.mdDoc "Extra config for `<Defaults>` section."; 110 }; 111 112 extraConfig = mkOption { 113 type = types.lines; 114 default = ""; 115 description = lib.mdDoc "Extra config for the global section."; 116 }; 117 118 servers = mkOption { 119 type = with types; attrsOf types.str; 120 default = {}; 121 example = { 122 node1 = "tcp://node1:3334"; 123 node2 = "tcp://node2:3334"; 124 }; 125 description = lib.mdDoc "URLs for storage server including port. The attribute names define the server alias."; 126 }; 127 128 fileSystems = mkOption { 129 description = lib.mdDoc '' 130 These options will create the `<FileSystem>` sections of config file. 131 ''; 132 default = { orangefs = {}; }; 133 example = literalExpression '' 134 { 135 fs1 = { 136 id = 101; 137 }; 138 139 fs2 = { 140 id = 102; 141 }; 142 } 143 ''; 144 type = with types; attrsOf (submodule ({ ... } : { 145 options = { 146 id = mkOption { 147 type = types.int; 148 default = 1; 149 description = lib.mdDoc "File system ID (must be unique within configuration)."; 150 }; 151 152 rootHandle = mkOption { 153 type = types.int; 154 default = 3; 155 description = lib.mdDoc "File system root ID."; 156 }; 157 158 extraConfig = mkOption { 159 type = types.lines; 160 default = ""; 161 description = lib.mdDoc "Extra config for `<FileSystem>` section."; 162 }; 163 164 troveSyncMeta = mkOption { 165 type = types.bool; 166 default = true; 167 description = lib.mdDoc "Sync meta data."; 168 }; 169 170 troveSyncData = mkOption { 171 type = types.bool; 172 default = false; 173 description = lib.mdDoc "Sync data."; 174 }; 175 176 extraStorageHints = mkOption { 177 type = types.lines; 178 default = ""; 179 description = lib.mdDoc "Extra config for `<StorageHints>` section."; 180 }; 181 }; 182 })); 183 }; 184 }; 185 }; 186 187 ###### implementation 188 189 config = mkIf cfg.enable { 190 environment.systemPackages = [ pkgs.orangefs ]; 191 192 # orangefs daemon will run as user 193 users.users.orangefs = { 194 isSystemUser = true; 195 group = "orangefs"; 196 }; 197 users.groups.orangefs = {}; 198 199 # To format the file system the config file is needed. 200 environment.etc."orangefs/server.conf" = { 201 text = configFile; 202 user = "orangefs"; 203 group = "orangefs"; 204 }; 205 206 systemd.services.orangefs-server = { 207 wantedBy = [ "multi-user.target" ]; 208 requires = [ "network-online.target" ]; 209 after = [ "network-online.target" ]; 210 211 serviceConfig = { 212 # Run as "simple" in foreground mode. 213 # This is more reliable 214 ExecStart = '' 215 ${pkgs.orangefs}/bin/pvfs2-server -d \ 216 /etc/orangefs/server.conf 217 ''; 218 TimeoutStopSec = "120"; 219 User = "orangefs"; 220 Group = "orangefs"; 221 }; 222 }; 223 }; 224 225}