402 lines
12 KiB
Nix
402 lines
12 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
options,
|
|
pkgs,
|
|
...
|
|
}: # XXX migration code for freeform settings: `options` can be removed in 2025
|
|
let
|
|
optionsGlobal = options;
|
|
in
|
|
|
|
let
|
|
|
|
inherit (lib.attrsets)
|
|
attrNames
|
|
attrValues
|
|
mapAttrsToList
|
|
removeAttrs
|
|
;
|
|
inherit (lib.lists)
|
|
all
|
|
allUnique
|
|
concatLists
|
|
concatMap
|
|
elem
|
|
isList
|
|
map
|
|
;
|
|
inherit (lib.modules) mkDefault mkIf;
|
|
inherit (lib.options) mkEnableOption mkOption mkPackageOption;
|
|
inherit (lib.strings)
|
|
concatLines
|
|
match
|
|
optionalString
|
|
toLower
|
|
;
|
|
inherit (lib.trivial) isInt;
|
|
inherit (lib.types)
|
|
addCheck
|
|
attrsOf
|
|
coercedTo
|
|
either
|
|
enum
|
|
int
|
|
lines
|
|
listOf
|
|
nonEmptyStr
|
|
nullOr
|
|
oneOf
|
|
path
|
|
port
|
|
singleLineStr
|
|
strMatching
|
|
submodule
|
|
;
|
|
|
|
scalarType =
|
|
# see the option's description below for the
|
|
# handling/transformation of each possible type
|
|
oneOf [
|
|
(enum [
|
|
true
|
|
null
|
|
])
|
|
int
|
|
path
|
|
singleLineStr
|
|
];
|
|
|
|
# TSM rejects servername strings longer than 64 chars.
|
|
servernameType = strMatching "[^[:space:]]{1,64}";
|
|
|
|
serverOptions =
|
|
{ name, config, ... }:
|
|
{
|
|
freeformType = attrsOf (either scalarType (listOf scalarType));
|
|
# Client system-options file directives are explained here:
|
|
# https://www.ibm.com/docs/en/storage-protect/8.1.24?topic=commands-processing-options
|
|
options.servername = mkOption {
|
|
type = servernameType;
|
|
default = name;
|
|
example = "mainTsmServer";
|
|
description = ''
|
|
Local name of the IBM TSM server,
|
|
must not contain space or more than 64 chars.
|
|
'';
|
|
};
|
|
options.tcpserveraddress = mkOption {
|
|
type = nonEmptyStr;
|
|
example = "tsmserver.company.com";
|
|
description = ''
|
|
Host/domain name or IP address of the IBM TSM server.
|
|
'';
|
|
};
|
|
options.tcpport = mkOption {
|
|
type = addCheck port (p: p <= 32767);
|
|
default = 1500; # official default
|
|
description = ''
|
|
TCP port of the IBM TSM server.
|
|
TSM does not support ports above 32767.
|
|
'';
|
|
};
|
|
options.nodename = mkOption {
|
|
type = nonEmptyStr;
|
|
example = "MY-TSM-NODE";
|
|
description = ''
|
|
Target node name on the IBM TSM server.
|
|
'';
|
|
};
|
|
options.genPasswd = mkEnableOption ''
|
|
automatic client password generation.
|
|
This option does *not* cause a line in
|
|
{file}`dsm.sys` by itself, but generates a
|
|
corresponding `passwordaccess` directive.
|
|
The password will be stored in the directory
|
|
given by the option {option}`passworddir`.
|
|
*Caution*:
|
|
If this option is enabled and the server forces
|
|
to renew the password (e.g. on first connection),
|
|
a random password will be generated and stored
|
|
'';
|
|
options.passwordaccess = mkOption {
|
|
type = enum [
|
|
"generate"
|
|
"prompt"
|
|
];
|
|
visible = false;
|
|
};
|
|
options.passworddir = mkOption {
|
|
type = nullOr path;
|
|
default = null;
|
|
example = "/home/alice/tsm-password";
|
|
description = ''
|
|
Directory that holds the TSM
|
|
node's password information.
|
|
'';
|
|
};
|
|
options.inclexcl = mkOption {
|
|
type = coercedTo lines (pkgs.writeText "inclexcl.dsm.sys") (nullOr path);
|
|
default = null;
|
|
example = ''
|
|
exclude.dir /nix/store
|
|
include.encrypt /home/.../*
|
|
'';
|
|
description = ''
|
|
Text lines with `include.*` and `exclude.*` directives
|
|
to be used when sending files to the IBM TSM server,
|
|
or an absolute path pointing to a file with such lines.
|
|
'';
|
|
};
|
|
config.commmethod = mkDefault "v6tcpip"; # uses v4 or v6, based on dns lookup result
|
|
config.passwordaccess = if config.genPasswd then "generate" else "prompt";
|
|
# XXX migration code for freeform settings, these can be removed in 2025:
|
|
options.warnings = optionsGlobal.warnings;
|
|
options.assertions = optionsGlobal.assertions;
|
|
imports =
|
|
let
|
|
inherit (lib.modules) mkRemovedOptionModule mkRenamedOptionModule;
|
|
in
|
|
[
|
|
(mkRemovedOptionModule [ "extraConfig" ]
|
|
"Please just add options directly to the server attribute set, cf. the description of `programs.tsmClient.servers`."
|
|
)
|
|
(mkRemovedOptionModule [ "text" ]
|
|
"Please just add options directly to the server attribute set, cf. the description of `programs.tsmClient.servers`."
|
|
)
|
|
(mkRenamedOptionModule [ "name" ] [ "servername" ])
|
|
(mkRenamedOptionModule [ "server" ] [ "tcpserveraddress" ])
|
|
(mkRenamedOptionModule [ "port" ] [ "tcpport" ])
|
|
(mkRenamedOptionModule [ "node" ] [ "nodename" ])
|
|
(mkRenamedOptionModule [ "passwdDir" ] [ "passworddir" ])
|
|
(mkRenamedOptionModule [ "includeExclude" ] [ "inclexcl" ])
|
|
];
|
|
};
|
|
|
|
options.programs.tsmClient = {
|
|
enable = mkEnableOption ''
|
|
IBM Storage Protect (Tivoli Storage Manager, TSM)
|
|
client command line applications with a
|
|
client system-options file "dsm.sys"
|
|
'';
|
|
servers = mkOption {
|
|
type = attrsOf (submodule serverOptions);
|
|
default = { };
|
|
example.mainTsmServer = {
|
|
tcpserveraddress = "tsmserver.company.com";
|
|
nodename = "MY-TSM-NODE";
|
|
compression = "yes";
|
|
};
|
|
description = ''
|
|
Server definitions ("stanzas")
|
|
for the client system-options file.
|
|
The name of each entry will be used for
|
|
the internal `servername` by default.
|
|
Each attribute will be transformed into a line
|
|
with a key-value pair within the server's stanza.
|
|
Integers as values will be
|
|
canonically turned into strings.
|
|
The boolean value `true` will be turned
|
|
into a line with just the attribute's name.
|
|
The value `null` will not generate a line.
|
|
A list as values generates an entry for
|
|
each value, according to the rules above.
|
|
'';
|
|
};
|
|
defaultServername = mkOption {
|
|
type = nullOr servernameType;
|
|
default = null;
|
|
example = "mainTsmServer";
|
|
description = ''
|
|
If multiple server stanzas are declared with
|
|
{option}`programs.tsmClient.servers`,
|
|
this option may be used to name a default
|
|
server stanza that IBM TSM uses in the absence of
|
|
a user-defined {file}`dsm.opt` file.
|
|
This option translates to a
|
|
`defaultserver` configuration line.
|
|
'';
|
|
};
|
|
dsmSysText = mkOption {
|
|
type = lines;
|
|
readOnly = true;
|
|
description = ''
|
|
This configuration key contains the effective text
|
|
of the client system-options file "dsm.sys".
|
|
It should not be changed, but may be
|
|
used to feed the configuration into other
|
|
TSM-depending packages used on the system.
|
|
'';
|
|
};
|
|
package = mkPackageOption pkgs "tsm-client" {
|
|
example = "tsm-client-withGui";
|
|
extraDescription = ''
|
|
It will be used with `.override`
|
|
to add paths to the client system-options file.
|
|
'';
|
|
};
|
|
wrappedPackage =
|
|
mkPackageOption pkgs "tsm-client" {
|
|
default = null;
|
|
extraDescription = ''
|
|
This option is to provide the effective derivation,
|
|
wrapped with the path to the
|
|
client system-options file "dsm.sys".
|
|
It should not be changed, but exists
|
|
for other modules that want to call TSM executables.
|
|
'';
|
|
}
|
|
// {
|
|
readOnly = true;
|
|
};
|
|
};
|
|
|
|
cfg = config.programs.tsmClient;
|
|
servernames = map (s: s.servername) (attrValues cfg.servers);
|
|
|
|
assertions =
|
|
[
|
|
{
|
|
assertion = allUnique (map toLower servernames);
|
|
message = ''
|
|
TSM server names
|
|
(option `programs.tsmClient.servers`)
|
|
contain duplicate name
|
|
(note that server names are case insensitive).
|
|
'';
|
|
}
|
|
{
|
|
assertion = (cfg.defaultServername != null) -> (elem cfg.defaultServername servernames);
|
|
message = ''
|
|
TSM default server name
|
|
`programs.tsmClient.defaultServername="${cfg.defaultServername}"`
|
|
not found in server names in
|
|
`programs.tsmClient.servers`.
|
|
'';
|
|
}
|
|
]
|
|
++ (mapAttrsToList (name: serverCfg: {
|
|
assertion = all (key: null != match "[^[:space:]]+" key) (attrNames serverCfg);
|
|
message = ''
|
|
TSM server setting names in
|
|
`programs.tsmClient.servers.${name}.*`
|
|
contain spaces, but that's not allowed.
|
|
'';
|
|
}) cfg.servers)
|
|
++ (mapAttrsToList (name: serverCfg: {
|
|
assertion = allUnique (map toLower (attrNames serverCfg));
|
|
message = ''
|
|
TSM server setting names in
|
|
`programs.tsmClient.servers.${name}.*`
|
|
contain duplicate names
|
|
(note that setting names are case insensitive).
|
|
'';
|
|
}) cfg.servers)
|
|
# XXX migration code for freeform settings, this can be removed in 2025:
|
|
++ (enrichMigrationInfos "assertions" (
|
|
addText:
|
|
{ assertion, message }:
|
|
{
|
|
inherit assertion;
|
|
message = addText message;
|
|
}
|
|
));
|
|
|
|
makeDsmSysLines =
|
|
key: value:
|
|
# Turn a key-value pair from the server options attrset
|
|
# into zero (value==null), one (scalar value) or
|
|
# more (value is list) configuration stanza lines.
|
|
if isList value then
|
|
concatMap (makeDsmSysLines key) value
|
|
# recurse into list
|
|
else if value == null then
|
|
[ ]
|
|
# skip `null` value
|
|
else
|
|
[
|
|
(
|
|
" ${key}${
|
|
if value == true then
|
|
""
|
|
# just output key if value is `true`
|
|
else if isInt value then
|
|
" ${builtins.toString value}"
|
|
else if path.check value then
|
|
" \"${value}\""
|
|
# enclose path in ".."
|
|
else if singleLineStr.check value then
|
|
" ${value}"
|
|
else
|
|
throw "assertion failed: cannot convert type" # should never happen
|
|
}"
|
|
)
|
|
];
|
|
|
|
makeDsmSysStanza =
|
|
{ servername, ... }@serverCfg:
|
|
let
|
|
# drop special values that should not go into server config block
|
|
attrs = removeAttrs serverCfg [
|
|
"servername"
|
|
"genPasswd"
|
|
# XXX migration code for freeform settings, these can be removed in 2025:
|
|
"assertions"
|
|
"warnings"
|
|
"extraConfig"
|
|
"text"
|
|
"name"
|
|
"server"
|
|
"port"
|
|
"node"
|
|
"passwdDir"
|
|
"includeExclude"
|
|
];
|
|
in
|
|
''
|
|
servername ${servername}
|
|
${concatLines (concatLists (mapAttrsToList makeDsmSysLines attrs))}
|
|
'';
|
|
|
|
dsmSysText = ''
|
|
**** IBM Storage Protect (Tivoli Storage Manager)
|
|
**** client system-options file "dsm.sys".
|
|
**** Do not edit!
|
|
**** This file is generated by NixOS configuration.
|
|
|
|
${optionalString (cfg.defaultServername != null) "defaultserver ${cfg.defaultServername}"}
|
|
|
|
${concatLines (map makeDsmSysStanza (attrValues cfg.servers))}
|
|
'';
|
|
|
|
# XXX migration code for freeform settings, this can be removed in 2025:
|
|
enrichMigrationInfos =
|
|
what: how:
|
|
concatLists (
|
|
mapAttrsToList (
|
|
name: serverCfg:
|
|
map (how (text: "In `programs.tsmClient.servers.${name}`: ${text}")) serverCfg."${what}"
|
|
) cfg.servers
|
|
);
|
|
|
|
in
|
|
|
|
{
|
|
|
|
inherit options;
|
|
|
|
config = mkIf cfg.enable {
|
|
inherit assertions;
|
|
programs.tsmClient.dsmSysText = dsmSysText;
|
|
programs.tsmClient.wrappedPackage = cfg.package.override rec {
|
|
dsmSysCli = pkgs.writeText "dsm.sys" cfg.dsmSysText;
|
|
dsmSysApi = dsmSysCli;
|
|
};
|
|
environment.systemPackages = [ cfg.wrappedPackage ];
|
|
# XXX migration code for freeform settings, this can be removed in 2025:
|
|
warnings = enrichMigrationInfos "warnings" (addText: addText);
|
|
};
|
|
|
|
meta.maintainers = [ lib.maintainers.yarny ];
|
|
|
|
}
|