grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / network-filesystems / openafs / client.nix
blobe7c2b952632844056981895c6f0c68fa94dca054
1 { config, lib, pkgs, ... }:
3 # openafsMod, openafsBin, mkCellServDB
4 with import ./lib.nix { inherit config lib pkgs; };
6 let
7   inherit (lib) getBin literalExpression mkOption mkIf optionalString singleton types;
9   cfg = config.services.openafsClient;
11   cellServDB = pkgs.fetchurl {
12     url = "http://dl.central.org/dl/cellservdb/CellServDB.2018-05-14";
13     sha256 = "1wmjn6mmyy2r8p10nlbdzs4nrqxy8a9pjyrdciy5nmppg4053rk2";
14   };
16   clientServDB = pkgs.writeText "client-cellServDB-${cfg.cellName}" (mkCellServDB cfg.cellName cfg.cellServDB);
18   afsConfig = pkgs.runCommand "afsconfig" { preferLocalBuild = true; } ''
19     mkdir -p $out
20     echo ${cfg.cellName} > $out/ThisCell
21     cat ${cellServDB} ${clientServDB} > $out/CellServDB
22     echo "${cfg.mountPoint}:${cfg.cache.directory}:${toString cfg.cache.blocks}" > $out/cacheinfo
23   '';
27   ###### interface
29   options = {
31     services.openafsClient = {
33       enable = mkOption {
34         default = false;
35         type = types.bool;
36         description = "Whether to enable the OpenAFS client.";
37       };
39       afsdb = mkOption {
40         default = true;
41         type = types.bool;
42         description = "Resolve cells via AFSDB DNS records.";
43       };
45       cellName = mkOption {
46         default = "";
47         type = types.str;
48         description = "Cell name.";
49         example = "grand.central.org";
50       };
52       cellServDB = mkOption {
53         default = [];
54         type = with types; listOf (submodule { options = cellServDBConfig; });
55         description = ''
56           This cell's database server records, added to the global
57           CellServDB. See CellServDB(5) man page for syntax. Ignored when
58           `afsdb` is set to `true`.
59         '';
60         example = [
61           { ip = "1.2.3.4"; dnsname = "first.afsdb.server.dns.fqdn.org"; }
62           { ip = "2.3.4.5"; dnsname = "second.afsdb.server.dns.fqdn.org"; }
63         ];
64       };
66       cache = {
67         blocks = mkOption {
68           default = 100000;
69           type = types.int;
70           description = "Cache size in 1KB blocks.";
71         };
73         chunksize = mkOption {
74           default = 0;
75           type = types.ints.between 0 30;
76           description = ''
77             Size of each cache chunk given in powers of
78             2. `0` resets the chunk size to its default
79             values (13 (8 KB) for memcache, 18-20 (256 KB to 1 MB) for
80             diskcache). Maximum value is 30. Important performance
81             parameter. Set to higher values when dealing with large files.
82           '';
83         };
85         directory = mkOption {
86           default = "/var/cache/openafs";
87           type = types.str;
88           description = "Cache directory.";
89         };
91         diskless = mkOption {
92           default = false;
93           type = types.bool;
94           description = ''
95             Use in-memory cache for diskless machines. Has no real
96             performance benefit anymore.
97           '';
98         };
99       };
101       crypt = mkOption {
102         default = true;
103         type = types.bool;
104         description = "Whether to enable (weak) protocol encryption.";
105       };
107       daemons = mkOption {
108         default = 2;
109         type = types.int;
110         description = ''
111           Number of daemons to serve user requests. Numbers higher than 6
112           usually do no increase performance. Default is sufficient for up
113           to five concurrent users.
114         '';
115       };
117       fakestat = mkOption {
118         default = false;
119         type = types.bool;
120         description = ''
121           Return fake data on stat() calls. If `true`,
122           always do so. If `false`, only do so for
123           cross-cell mounts (as these are potentially expensive).
124         '';
125       };
127       inumcalc = mkOption {
128         default = "compat";
129         type = types.strMatching "compat|md5";
130         description = ''
131           Inode calculation method. `compat` is
132           computationally less expensive, but `md5` greatly
133           reduces the likelihood of inode collisions in larger scenarios
134           involving multiple cells mounted into one AFS space.
135         '';
136       };
138       mountPoint = mkOption {
139         default = "/afs";
140         type = types.str;
141         description = ''
142           Mountpoint of the AFS file tree, conventionally
143           `/afs`. When set to a different value, only
144           cross-cells that use the same value can be accessed.
145         '';
146       };
148       packages = {
149         module = mkOption {
150           default = config.boot.kernelPackages.openafs;
151           defaultText = literalExpression "config.boot.kernelPackages.openafs";
152           type = types.package;
153           description = "OpenAFS kernel module package. MUST match the userland package!";
154         };
155         programs = mkOption {
156           default = getBin pkgs.openafs;
157           defaultText = literalExpression "getBin pkgs.openafs";
158           type = types.package;
159           description = "OpenAFS programs package. MUST match the kernel module package!";
160         };
161       };
163       sparse = mkOption {
164         default = true;
165         type = types.bool;
166         description = "Minimal cell list in /afs.";
167       };
169       startDisconnected = mkOption {
170         default = false;
171         type = types.bool;
172         description = ''
173           Start up in disconnected mode.  You need to execute
174           `fs disco online` (as root) to switch to
175           connected mode. Useful for roaming devices.
176         '';
177       };
179     };
180   };
183   ###### implementation
185   config = mkIf cfg.enable {
187     assertions = [
188       { assertion = cfg.afsdb || cfg.cellServDB != [];
189         message = "You should specify all cell-local database servers in config.services.openafsClient.cellServDB or set config.services.openafsClient.afsdb.";
190       }
191       { assertion = cfg.cellName != "";
192         message = "You must specify the local cell name in config.services.openafsClient.cellName.";
193       }
194     ];
196     environment.systemPackages = [ openafsBin ];
198     environment.etc = {
199       clientCellServDB = {
200         source = pkgs.runCommand "CellServDB" { preferLocalBuild = true; } ''
201           cat ${cellServDB} ${clientServDB} > $out
202         '';
203         target = "openafs/CellServDB";
204         mode = "0644";
205       };
206       clientCell = {
207         text = ''
208           ${cfg.cellName}
209         '';
210         target = "openafs/ThisCell";
211         mode = "0644";
212       };
213     };
215     systemd.services.afsd = {
216       description = "AFS client";
217       wantedBy = [ "multi-user.target" ];
218       wants = lib.optional (!cfg.startDisconnected) "network-online.target";
219       after = singleton (if cfg.startDisconnected then  "network.target" else "network-online.target");
220       serviceConfig = { RemainAfterExit = true; };
221       restartIfChanged = false;
223       preStart = ''
224         mkdir -p -m 0755 ${cfg.mountPoint}
225         mkdir -m 0700 -p ${cfg.cache.directory}
226         ${pkgs.kmod}/bin/insmod ${openafsMod}/lib/modules/*/extra/openafs/libafs.ko.xz
227         ${openafsBin}/sbin/afsd \
228           -mountdir ${cfg.mountPoint} \
229           -confdir ${afsConfig} \
230           ${optionalString (!cfg.cache.diskless) "-cachedir ${cfg.cache.directory}"} \
231           -blocks ${toString cfg.cache.blocks} \
232           -chunksize ${toString cfg.cache.chunksize} \
233           ${optionalString cfg.cache.diskless "-memcache"} \
234           -inumcalc ${cfg.inumcalc} \
235           ${if cfg.fakestat then "-fakestat-all" else "-fakestat"} \
236           ${if cfg.sparse then "-dynroot-sparse" else "-dynroot"} \
237           ${optionalString cfg.afsdb "-afsdb"}
238         ${openafsBin}/bin/fs setcrypt ${if cfg.crypt then "on" else "off"}
239         ${optionalString cfg.startDisconnected "${openafsBin}/bin/fs discon offline"}
240       '';
242       # Doing this in preStop, because after these commands AFS is basically
243       # stopped, so systemd has nothing to do, just noticing it.  If done in
244       # postStop, then we get a hang + kernel oops, because AFS can't be
245       # stopped simply by sending signals to processes.
246       preStop = ''
247         ${pkgs.util-linux}/bin/umount ${cfg.mountPoint}
248         ${openafsBin}/sbin/afsd -shutdown
249         ${pkgs.kmod}/sbin/rmmod libafs
250       '';
251     };
252   };