openafs: improve CellServDB options and add test (#424753)

This commit is contained in:
Luke Granger-Brown
2026-01-06 21:08:35 +00:00
committed by GitHub
8 changed files with 293 additions and 65 deletions
@@ -21,19 +21,28 @@ let
cfg = config.services.openafsClient;
cellServDB = pkgs.fetchurl {
url = "http://dl.central.org/dl/cellservdb/CellServDB.2018-05-14";
sha256 = "1wmjn6mmyy2r8p10nlbdzs4nrqxy8a9pjyrdciy5nmppg4053rk2";
};
clientServDB = pkgs.writeText "client-cellServDB-${cfg.cellName}" (mkCellServDB cfg.cellServDB);
clientServDB = pkgs.writeText "client-cellServDB-${cfg.cellName}" (
mkCellServDB cfg.cellName cfg.cellServDB
);
cellServDB =
let
localCells = builtins.attrNames cfg.cellServDB;
localCellsRegex = lib.concatMapStringsSep "\\|" (lib.replaceStrings [ "." ] [ "\\." ]) localCells;
sedExpr = '':x /^>\(${localCellsRegex}\) / { n; :y /^>/! { n; by }; bx }; p'';
globalCommand =
if cfg.cellServDB != { } then
''sed -n -e ${lib.escapeShellArg sedExpr} ${cfg.globalCellServDBFile}''
else
''cat ${cfg.globalCellServDBFile}'';
in
pkgs.runCommand "CellServDB" { preferLocalBuild = true; } ''
${lib.optionalString (cfg.globalCellServDBFile != null) ''${globalCommand} > $out''}
cat ${clientServDB} >> $out
'';
afsConfig = pkgs.runCommand "afsconfig" { preferLocalBuild = true; } ''
mkdir -p $out
echo ${cfg.cellName} > $out/ThisCell
cat ${cellServDB} ${clientServDB} > $out/CellServDB
cp ${cellServDB} $out/CellServDB
echo "${cfg.mountPoint}:${cfg.cache.directory}:${toString cfg.cache.blocks}" > $out/cacheinfo
'';
@@ -64,28 +73,38 @@ in
example = "grand.central.org";
};
globalCellServDBFile = mkOption {
default = pkgs.openafs.cellservdb;
defaultText = literalExpression "pkgs.openafs.cellservdb";
type = types.nullOr types.pathInStore;
description = ''
Global CellServDB file to be deployed. Set to `null` to only deploy the
cells in `cellServDB`. Any cells defined in `cellServDB` will override
cells in the global file.
'';
example = lib.literalExpression "./CellServDB";
};
cellServDB = mkOption {
default = [ ];
type =
with types;
listOf (submodule {
options = cellServDBConfig;
});
default = { };
type = cellServDBType cfg.cellName;
description = ''
This cell's database server records, added to the global
CellServDB. See {manpage}`CellServDB(5)` man page for syntax. Ignored when
`afsdb` is set to `true`.
'';
example = [
{
ip = "1.2.3.4";
dnsname = "first.afsdb.server.dns.fqdn.org";
}
{
ip = "2.3.4.5";
dnsname = "second.afsdb.server.dns.fqdn.org";
}
];
example = {
"dns.fqdn.org" = [
{
ip = "1.2.3.4";
dnsname = "first.afsdb.server.dns.fqdn.org";
}
{
ip = "2.3.4.5";
dnsname = "second.afsdb.server.dns.fqdn.org";
}
];
};
};
cache = {
@@ -223,9 +242,7 @@ in
environment.etc = {
clientCellServDB = {
source = pkgs.runCommand "CellServDB" { preferLocalBuild = true; } ''
cat ${cellServDB} ${clientServDB} > $out
'';
source = cellServDB;
target = "openafs/CellServDB";
mode = "0644";
};
@@ -3,15 +3,32 @@
let
inherit (lib)
concatStringsSep
concatMapAttrsStringSep
mkOption
types
optionalString
;
cellServDBMemberType = types.submodule {
options = {
ip = mkOption {
type = types.str;
default = "";
example = "1.2.3.4";
description = "IP Address of a database server";
};
dnsname = mkOption {
type = types.str;
default = "";
example = "afs.example.org";
description = "DNS full-qualified domain name of a database server";
};
};
};
cellServDBCellType = types.listOf cellServDBMemberType;
in
{
mkCellServDB =
mkCellServDB = concatMapAttrsStringSep "" (
cellName: db:
''
>${cellName}
@@ -19,23 +36,15 @@ in
+ (concatStringsSep "\n" (
map (dbm: optionalString (dbm.ip != "" && dbm.dnsname != "") "${dbm.ip} #${dbm.dnsname}") db
))
+ "\n";
+ "\n"
);
# CellServDB configuration type
cellServDBConfig = {
ip = mkOption {
type = types.str;
default = "";
example = "1.2.3.4";
description = "IP Address of a database server";
};
dnsname = mkOption {
type = types.str;
default = "";
example = "afs.example.org";
description = "DNS full-qualified domain name of a database server";
};
};
cellServDBType =
thisCell:
types.coercedTo (types.listOf types.anything) (m: { "${thisCell}" = m; }) (
types.attrsOf cellServDBCellType
);
openafsMod = config.services.openafsClient.packages.module;
openafsBin = config.services.openafsClient.packages.programs;
@@ -67,10 +67,10 @@ let
null;
buCellServDB = pkgs.writeText "backup-cellServDB-${cfg.cellName}" (
mkCellServDB cfg.cellName cfg.roles.backup.cellServDB
mkCellServDB cfg.roles.backup.cellServDB
);
useBuCellServDB = (cfg.roles.backup.cellServDB != [ ]) && (!cfg.roles.backup.enableFabs);
useBuCellServDB = (cfg.roles.backup.cellServDB != { }) && (!cfg.roles.backup.enableFabs);
cfg = config.services.openafsServer;
@@ -128,9 +128,22 @@ in
};
cellServDB = mkOption {
default = [ ];
type = with types; listOf (submodule [ { options = cellServDBConfig; } ]);
description = "Definition of all cell-local database server machines.";
default = { };
type = cellServDBType cfg.cellName;
description = ''
Definition of all cell-local database server machines. If a single
list is provided, it will be used as the servers for `cellName`.
'';
example = [
{
ip = "1.2.3.4";
dnsname = "first.afsdb.server.dns.fqdn.org";
}
{
ip = "2.3.4.5";
dnsname = "second.afsdb.server.dns.fqdn.org";
}
];
};
package = mkPackageOption pkgs "openafs" { };
@@ -226,8 +239,8 @@ in
};
cellServDB = mkOption {
default = [ ];
type = with types; listOf (submodule [ { options = cellServDBConfig; } ]);
default = { };
type = cellServDBType cfg.cellName;
description = ''
Definition of all cell-local backup database server machines.
Use this when your cell uses less backup database servers than
@@ -288,9 +301,19 @@ in
config = mkIf cfg.enable {
warnings =
lib.optional ((builtins.attrNames cfg.cellServDB) != [ cfg.cellName ]) ''
config.services.openafsServer.cellServDB should normally only contain servers for one cell. It currently contains servers for ${builtins.toString (builtins.attrNames cfg.cellServDB)}.
''
++
lib.optional (useBuCellServDB && (builtins.attrNames cfg.backup.cellServDB) != [ cfg.cellName ])
''
config.services.openafsServer.backup.cellServDB should normally only contain servers for one cell. It currently contains servers for ${builtins.toString (builtins.attrNames cfg.cellServDB)}.
'';
assertions = [
{
assertion = cfg.cellServDB != [ ];
assertion = cfg.cellServDB != { } && (cfg.cellServDB."${cfg.cellName}" or [ ]) != [ ];
message = "You must specify all cell-local database servers in config.services.openafsServer.cellServDB.";
}
{
@@ -308,7 +331,7 @@ in
mode = "0644";
};
cellServDB = {
text = mkCellServDB cfg.cellName cfg.cellServDB;
text = mkCellServDB cfg.cellServDB;
target = "openafs/server/CellServDB";
mode = "0644";
};
@@ -319,8 +342,9 @@ in
};
buCellServDB = {
enable = useBuCellServDB;
text = mkCellServDB cfg.cellName cfg.roles.backup.cellServDB;
text = mkCellServDB cfg.roles.backup.cellServDB;
target = "openafs/backup/CellServDB";
mode = "0644";
};
};
@@ -336,7 +360,6 @@ in
preStart = ''
mkdir -m 0755 -p /var/openafs
${optionalString (netInfo != null) "cp ${netInfo} /var/openafs/netInfo"}
${optionalString useBuCellServDB "cp ${buCellServDB}"}
'';
serviceConfig = {
ExecStart = "${openafsBin}/bin/bosserver -nofork";
+1
View File
@@ -1155,6 +1155,7 @@ in
onlyoffice = runTest ./onlyoffice.nix;
open-web-calendar = runTest ./web-apps/open-web-calendar.nix;
open-webui = runTest ./open-webui.nix;
openafs = runTest ./openafs.nix;
openarena = runTest ./openarena.nix;
openbao = runTest ./openbao.nix;
opencloud = runTest ./opencloud.nix;
+139
View File
@@ -0,0 +1,139 @@
{ lib, ... }:
let
cellName = "cell.com";
realmName = lib.toUpper cellName;
in
{
name = "openafs";
defaults =
{ config, nodes, ... }:
let
cellServDB = {
${cellName} = lib.mapAttrsToList (_: config: {
ip = config.networking.primaryIPAddress;
dnsname = "${config.networking.hostName}.${config.networking.domain}";
}) nodes;
};
in
{
config = {
virtualisation.vlans = [ 1 ];
networking.useDHCP = false; # Disable external connectivity
networking.domain = cellName;
networking.firewall.enable = false;
security.krb5 = {
enable = true;
settings.libdefaults.default_realm = realmName;
};
services.openafsClient = {
enable = true;
inherit cellName;
cellServDB = cellServDB // {
"grand.central.org" = [
{
ip = "1.1.1.1";
dnsname = "one.grand.central.org";
}
];
};
};
services.openafsServer = {
enable = true;
inherit cellName cellServDB;
};
security.krb5.settings.realms.${realmName} = {
admin_server = nodes.a.networking.primaryIPAddress;
kdc = [ nodes.a.networking.primaryIPAddress ];
};
};
};
nodes.a = {
services.kerberos_server = {
enable = true;
settings.realms."CELL.COM".acl = [
{
principal = "admin";
access = [
"add"
"cpw"
];
}
];
};
};
nodes.b = { };
testScript = ''
import re
def setup_kdc(machine):
# Set up realm
machine.succeed(
"kdb5_util create -s -r CELL.COM -P master_key",
"systemctl restart kadmind.service kdc.service",
)
for unit in ["kadmind", "kdc"]:
machine.wait_for_unit(f"{unit}.service")
# Create admin and afs/ principals
machine.succeed(
"kadmin.local add_principal -randkey admin",
"rm -f /tmp/shared/admin.keytab",
"kadmin.local ktadd -k /tmp/shared/admin.keytab admin",
"kadmin.local add_principal -randkey -e aes256-cts-hmac-sha1-96:normal,aes128-cts-hmac-sha1-96:normal afs/${cellName}",
)
# Generate a keytab for afs/
out = machine.succeed(
"rm -f /tmp/shared/rxkad.keytab",
"kadmin.local ktadd -k /tmp/shared/rxkad.keytab -e aes256-cts-hmac-sha1-96:normal,aes128-cts-hmac-sha1-96:normal afs/${cellName}",
)
m = re.search(r"kvno (\d+)", out)
assert m
kvno = m.group(1)
return kvno
def setup_afs_daemons(machine, kvno):
machine.succeed(
"mkdir -p /vicepa",
"touch /vicepa/AlwaysAttach",
)
machine.succeed(*(
f"asetkey add rxkad_krb5 {kvno} {enctype} /tmp/shared/rxkad.keytab afs/${cellName}"
for enctype in (18, 17)
))
machine.start_job("openafs-server")
machine.wait_until_succeeds("bos adduser localhost admin -localauth")
start_all()
kvno = setup_kdc(a)
setup_afs_daemons(a, kvno)
setup_afs_daemons(b, kvno)
# Create initial 'admin' user
# N.B. It can take 60+ seconds before the ptserver has quorum and accepts writes, so try a few times until it succeeds
a.wait_until_succeeds("pts createuser -name admin -localauth")
a.succeed(
"pts adduser admin system:administrators -localauth",
)
# Create initial volumes
a.wait_until_succeeds("vos listvol localhost -localauth")
a.succeed(
"vos create localhost vicepa root.afs -localauth",
"vos create localhost vicepa root.cell -localauth",
)
# Authenticate and add a file
a.succeed(
"kinit -k -t /tmp/shared/admin.keytab admin",
"aklog",
"echo pass > /afs/${cellName}/test.txt",
)
# Authenticate and read a file
b.succeed(
"kinit -k -t /tmp/shared/admin.keytab admin",
"aklog",
)
assert b.succeed("cat /afs/${cellName}/test.txt").strip() == "pass"
# Ensure the global CellServDB can be overridden.
assert a.succeed("fs listcells -n | grep grand.central.org").strip() == "Cell grand.central.org on hosts 1.1.1.1."
'';
}
+4 -1
View File
@@ -1,5 +1,6 @@
{
lib,
callPackage,
stdenv,
buildPackages,
fetchurl,
@@ -103,7 +104,7 @@ stdenv.mkDerivation {
)
''
+ optionalString withTsm ''
export XBSA_CFLAGS="-Dxbsa -DNEW_XBSA -I${tsm-client}/lib64/sample -DXBSA_TSMLIB=\\\"${tsm-client}/lib64/libApiTSM64.so\\\""
export XBSA_CFLAGS="-Dxbsa -DNEW_XBSA -I${tsm-client}/opt/tivoli/tsm/client/api/bin64/sample -DXBSA_TSMLIB=\\\"${tsm-client}/lib64/libApiTSM64.so\\\""
'';
buildFlags = [ "all_nolibafs" ];
@@ -143,6 +144,8 @@ stdenv.mkDerivation {
done
'';
passthru.cellservdb = callPackage ../cellservdb.nix { };
meta = {
outputsToInstall = [
"out"
+22 -9
View File
@@ -1,18 +1,31 @@
diff -ru openafs-1.8.0/src/butc/afsxbsa.c openafs-1.8.0.new/src/butc/afsxbsa.c
--- openafs-1.8.0/src/butc/afsxbsa.c 2018-04-06 03:21:12.000000000 +0200
+++ openafs-1.8.0.new/src/butc/afsxbsa.c 2018-06-12 16:26:26.272522305 +0200
@@ -651,7 +651,7 @@
diff -ru openafs-1.8.13.2/src/butc/afsxbsa.c openafs-1.8.13.2.new/src/butc/afsxbsa.c
--- openafs-1.8.13.2/src/butc/afsxbsa.c 2025-01-23 12:12:55.000000000 -0500
+++ openafs-1.8.13.2.new/src/butc/afsxbsa.c 2025-07-13 19:30:19.513895421 -0400
@@ -631,7 +631,7 @@
#if defined(AFS_AIX_ENV)
dynlib = dlopen("/usr/lib/libApiDS.a(dsmapish.o)", RTLD_NOW | RTLD_LOCAL | RTLD_MEMBER);
#elif defined (AFS_AMD64_LINUX26_ENV)
#elif defined (AFS_AMD64_LINUX_ENV)
- dynlib = dlopen("/usr/lib64/libApiTSM64.so", RTLD_NOW | RTLD_LOCAL);
+ dynlib = dlopen(XBSA_TSMLIB, RTLD_NOW | RTLD_LOCAL);
#elif defined(AFS_SUN5_ENV) || defined(AFS_LINUX26_ENV)
#elif defined(AFS_SUN5_ENV) || defined(AFS_LINUX_ENV)
dynlib = dlopen("/usr/lib/libApiDS.so", RTLD_NOW | RTLD_LOCAL);
#else
diff -ru openafs-1.8.0/src/cf/tivoli.m4 openafs-1.8.0.new/src/cf/tivoli.m4
--- openafs-1.8.0/src/cf/tivoli.m4 2018-04-06 03:21:12.000000000 +0200
+++ openafs-1.8.0.new/src/cf/tivoli.m4 2018-06-12 16:26:26.072522485 +0200
diff -ru openafs-1.8.13.2/src/butc/lwps.c openafs-1.8.13.2.new/src/butc/lwps.c
--- openafs-1.8.13.2/src/butc/lwps.c 2025-01-23 12:12:55.000000000 -0500
+++ openafs-1.8.13.2.new/src/butc/lwps.c 2025-07-14 01:02:51.409677422 -0400
@@ -53,7 +53,7 @@
/* XBSA Global Parameters */
extern afs_int32 xbsaType;
#ifdef xbsa
-struct butx_transactionInfo butxInfo;
+extern struct butx_transactionInfo butxInfo;
#endif
static struct TapeBlock { /* A 16KB tapeblock */
Only in openafs-1.8.13.2.new/src/butc: lwps.c~
diff -ru openafs-1.8.13.2/src/cf/tivoli.m4 openafs-1.8.13.2.new/src/cf/tivoli.m4
--- openafs-1.8.13.2/src/cf/tivoli.m4 2025-01-23 12:12:55.000000000 -0500
+++ openafs-1.8.13.2.new/src/cf/tivoli.m4 2025-07-13 19:30:39.350794275 -0400
@@ -1,45 +1,7 @@
AC_DEFUN([OPENAFS_TIVOLI_TESTS],[
dnl check for tivoli
+23
View File
@@ -0,0 +1,23 @@
{
fetchurl,
lib,
}:
let
version = "2025-08-16";
in
fetchurl {
pname = "cellservdb";
inherit version;
url = "https://grand.central.org/dl/cellservdb/CellServDB.${version}";
hash = "sha256-Rb6DBb9to7x5ya2ywx+wTls2c9GQmV0v5z9rmZ78dDs=";
meta = {
description = "GRAND.CENTRAL.ORG Public CellServDB";
homepage = "https://grand.central.org/csdb.html";
maintainers = [
lib.maintainers.quentin
];
# As a database, CellServDB is not copywritten.
license = lib.licenses.publicDomain;
};
}