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