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 "OrangeFS server";
78
79 logType = mkOption {
80 type = with types; enum [ "file" "syslog" ];
81 default = "syslog";
82 description = "Destination for log messages.";
83 };
84
85 dataStorageSpace = mkOption {
86 type = types.nullOr types.str;
87 default = null;
88 example = "/data/storage";
89 description = "Directory for data storage.";
90 };
91
92 metadataStorageSpace = mkOption {
93 type = types.nullOr types.str;
94 default = null;
95 example = "/data/meta";
96 description = "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 = "List of BMI modules to load.";
104 };
105
106 extraDefaults = mkOption {
107 type = types.lines;
108 default = "";
109 description = "Extra config for <literal><Defaults></literal> section.";
110 };
111
112 extraConfig = mkOption {
113 type = types.lines;
114 default = "";
115 description = "Extra config for the global section.";
116 };
117
118 servers = mkOption {
119 type = with types; attrsOf types.str;
120 default = {};
121 example = ''
122 {
123 node1="tcp://node1:3334";
124 node2="tcp://node2:3334";
125 }
126 '';
127 description = "URLs for storage server including port. The attribute names define the server alias.";
128 };
129
130 fileSystems = mkOption {
131 description = ''
132 These options will create the <literal><FileSystem></literal> sections of config file.
133 '';
134 default = { orangefs = {}; };
135 defaultText = literalExample "{ orangefs = {}; }";
136 example = literalExample ''
137 {
138 fs1 = {
139 id = 101;
140 };
141
142 fs2 = {
143 id = 102;
144 };
145 }
146 '';
147 type = with types; attrsOf (submodule ({ ... } : {
148 options = {
149 id = mkOption {
150 type = types.int;
151 default = 1;
152 description = "File system ID (must be unique within configuration).";
153 };
154
155 rootHandle = mkOption {
156 type = types.int;
157 default = 3;
158 description = "File system root ID.";
159 };
160
161 extraConfig = mkOption {
162 type = types.lines;
163 default = "";
164 description = "Extra config for <literal><FileSystem></literal> section.";
165 };
166
167 troveSyncMeta = mkOption {
168 type = types.bool;
169 default = true;
170 description = "Sync meta data.";
171 };
172
173 troveSyncData = mkOption {
174 type = types.bool;
175 default = false;
176 description = "Sync data.";
177 };
178
179 extraStorageHints = mkOption {
180 type = types.lines;
181 default = "";
182 description = "Extra config for <literal><StorageHints></literal> section.";
183 };
184 };
185 }));
186 };
187 };
188 };
189
190 ###### implementation
191
192 config = mkIf cfg.enable {
193 environment.systemPackages = [ pkgs.orangefs ];
194
195 # orangefs daemon will run as user
196 users.users.orangefs.isSystemUser = true;
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 forground 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}