Merge pull request #61312 from Yarny0/tsm-client

TSM client

authored by Florian Klink and committed by GitHub 9d339e3b ce247b4f

+563
+2
nixos/modules/module-list.nix
··· 139 139 ./programs/sway.nix 140 140 ./programs/thefuck.nix 141 141 ./programs/tmux.nix 142 + ./programs/tsm-client.nix 142 143 ./programs/udevil.nix 143 144 ./programs/venus.nix 144 145 ./programs/vim.nix ··· 210 211 ./services/backup/restic-rest-server.nix 211 212 ./services/backup/rsnapshot.nix 212 213 ./services/backup/tarsnap.nix 214 + ./services/backup/tsm.nix 213 215 ./services/backup/znapzend.nix 214 216 ./services/cluster/hadoop/default.nix 215 217 ./services/cluster/kubernetes/addons/dns.nix
+287
nixos/modules/programs/tsm-client.nix
··· 1 + { config, lib, pkgs, ... }: 2 + 3 + let 4 + 5 + inherit (builtins) length map; 6 + inherit (lib.attrsets) attrNames filterAttrs hasAttr mapAttrs mapAttrsToList optionalAttrs; 7 + inherit (lib.modules) mkDefault mkIf; 8 + inherit (lib.options) literalExample mkEnableOption mkOption; 9 + inherit (lib.strings) concatStringsSep optionalString toLower; 10 + inherit (lib.types) addCheck attrsOf lines loaOf nullOr package path port str strMatching submodule; 11 + 12 + # Checks if given list of strings contains unique 13 + # elements when compared without considering case. 14 + # Type: checkIUnique :: [string] -> bool 15 + # Example: checkIUnique ["foo" "Foo"] => false 16 + checkIUnique = lst: 17 + let 18 + lenUniq = l: length (lib.lists.unique l); 19 + in 20 + lenUniq lst == lenUniq (map toLower lst); 21 + 22 + # TSM rejects servername strings longer than 64 chars. 23 + servernameType = strMatching ".{1,64}"; 24 + 25 + serverOptions = { name, config, ... }: { 26 + options.name = mkOption { 27 + type = servernameType; 28 + example = "mainTsmServer"; 29 + description = '' 30 + Local name of the IBM TSM server, 31 + must be uncapitalized and no longer than 64 chars. 32 + The value will be used for the 33 + <literal>server</literal> 34 + directive in <filename>dsm.sys</filename>. 35 + ''; 36 + }; 37 + options.server = mkOption { 38 + type = strMatching ".+"; 39 + example = "tsmserver.company.com"; 40 + description = '' 41 + Host/domain name or IP address of the IBM TSM server. 42 + The value will be used for the 43 + <literal>tcpserveraddress</literal> 44 + directive in <filename>dsm.sys</filename>. 45 + ''; 46 + }; 47 + options.port = mkOption { 48 + type = addCheck port (p: p<=32767); 49 + default = 1500; # official default 50 + description = '' 51 + TCP port of the IBM TSM server. 52 + The value will be used for the 53 + <literal>tcpport</literal> 54 + directive in <filename>dsm.sys</filename>. 55 + TSM does not support ports above 32767. 56 + ''; 57 + }; 58 + options.node = mkOption { 59 + type = strMatching ".+"; 60 + example = "MY-TSM-NODE"; 61 + description = '' 62 + Target node name on the IBM TSM server. 63 + The value will be used for the 64 + <literal>nodename</literal> 65 + directive in <filename>dsm.sys</filename>. 66 + ''; 67 + }; 68 + options.genPasswd = mkEnableOption '' 69 + automatic client password generation. 70 + This option influences the 71 + <literal>passwordaccess</literal> 72 + directive in <filename>dsm.sys</filename>. 73 + The password will be stored in the directory 74 + given by the option <option>passwdDir</option>. 75 + <emphasis>Caution</emphasis>: 76 + If this option is enabled and the server forces 77 + to renew the password (e.g. on first connection), 78 + a random password will be generated and stored 79 + ''; 80 + options.passwdDir = mkOption { 81 + type = path; 82 + example = "/home/alice/tsm-password"; 83 + description = '' 84 + Directory that holds the TSM 85 + node's password information. 86 + The value will be used for the 87 + <literal>passworddir</literal> 88 + directive in <filename>dsm.sys</filename>. 89 + ''; 90 + }; 91 + options.includeExclude = mkOption { 92 + type = lines; 93 + default = ""; 94 + example = '' 95 + exclude.dir /nix/store 96 + include.encrypt /home/.../* 97 + ''; 98 + description = '' 99 + <literal>include.*</literal> and 100 + <literal>exclude.*</literal> directives to be 101 + used when sending files to the IBM TSM server. 102 + The lines will be written into a file that the 103 + <literal>inclexcl</literal> 104 + directive in <filename>dsm.sys</filename> points to. 105 + ''; 106 + }; 107 + options.extraConfig = mkOption { 108 + # TSM option keys are case insensitive; 109 + # we have to ensure there are no keys that 110 + # differ only by upper and lower case. 111 + type = addCheck 112 + (attrsOf (nullOr str)) 113 + (attrs: checkIUnique (attrNames attrs)); 114 + default = {}; 115 + example.compression = "yes"; 116 + example.passwordaccess = null; 117 + description = '' 118 + Additional key-value pairs for the server stanza. 119 + Values must be strings, or <literal>null</literal> 120 + for the key not to be used in the stanza 121 + (e.g. to overrule values generated by other options). 122 + ''; 123 + }; 124 + options.text = mkOption { 125 + type = lines; 126 + example = literalExample 127 + ''lib.modules.mkAfter "compression no"''; 128 + description = '' 129 + Additional text lines for the server stanza. 130 + This option can be used if certion configuration keys 131 + must be used multiple times or ordered in a certain way 132 + as the <option>extraConfig</option> option can't 133 + control the order of lines in the resulting stanza. 134 + Note that the <literal>server</literal> 135 + line at the beginning of the stanza is 136 + not part of this option's value. 137 + ''; 138 + }; 139 + options.stanza = mkOption { 140 + type = str; 141 + internal = true; 142 + visible = false; 143 + description = "Server stanza text generated from the options."; 144 + }; 145 + config.name = mkDefault name; 146 + # Client system-options file directives are explained here: 147 + # https://www.ibm.com/support/knowledgecenter/SSEQVQ_8.1.8/client/c_opt_usingopts.html 148 + config.extraConfig = 149 + mapAttrs (lib.trivial.const mkDefault) ( 150 + { 151 + commmethod = "v6tcpip"; # uses v4 or v6, based on dns lookup result 152 + tcpserveraddress = config.server; 153 + tcpport = builtins.toString config.port; 154 + nodename = config.node; 155 + passwordaccess = if config.genPasswd then "generate" else "prompt"; 156 + passworddir = ''"${config.passwdDir}"''; 157 + } // optionalAttrs (config.includeExclude!="") { 158 + inclexcl = ''"${pkgs.writeText "inclexcl.dsm.sys" config.includeExclude}"''; 159 + } 160 + ); 161 + config.text = 162 + let 163 + attrset = filterAttrs (k: v: v!=null) config.extraConfig; 164 + mkLine = k: v: k + optionalString (v!="") " ${v}"; 165 + lines = mapAttrsToList mkLine attrset; 166 + in 167 + concatStringsSep "\n" lines; 168 + config.stanza = '' 169 + server ${config.name} 170 + ${config.text} 171 + ''; 172 + }; 173 + 174 + options.programs.tsmClient = { 175 + enable = mkEnableOption '' 176 + IBM Spectrum Protect (Tivoli Storage Manager, TSM) 177 + client command line applications with a 178 + client system-options file "dsm.sys" 179 + ''; 180 + servers = mkOption { 181 + type = loaOf (submodule [ serverOptions ]); 182 + default = {}; 183 + example.mainTsmServer = { 184 + server = "tsmserver.company.com"; 185 + node = "MY-TSM-NODE"; 186 + extraConfig.compression = "yes"; 187 + }; 188 + description = '' 189 + Server definitions ("stanzas") 190 + for the client system-options file. 191 + ''; 192 + }; 193 + defaultServername = mkOption { 194 + type = nullOr servernameType; 195 + default = null; 196 + example = "mainTsmServer"; 197 + description = '' 198 + If multiple server stanzas are declared with 199 + <option>programs.tsmClient.servers</option>, 200 + this option may be used to name a default 201 + server stanza that IBM TSM uses in the absence of 202 + a user-defined <filename>dsm.opt</filename> file. 203 + This option translates to a 204 + <literal>defaultserver</literal> configuration line. 205 + ''; 206 + }; 207 + dsmSysText = mkOption { 208 + type = lines; 209 + readOnly = true; 210 + description = '' 211 + This configuration key contains the effective text 212 + of the client system-options file "dsm.sys". 213 + It should not be changed, but may be 214 + used to feed the configuration into other 215 + TSM-depending packages used on the system. 216 + ''; 217 + }; 218 + package = mkOption { 219 + type = package; 220 + default = pkgs.tsm-client; 221 + defaultText = "pkgs.tsm-client"; 222 + example = literalExample "pkgs.tsm-client-withGui"; 223 + description = '' 224 + The TSM client derivation to be 225 + added to the system environment. 226 + It will called with <literal>.override</literal> 227 + to add paths to the client system-options file. 228 + ''; 229 + }; 230 + wrappedPackage = mkOption { 231 + type = package; 232 + readOnly = true; 233 + description = '' 234 + The TSM client derivation, wrapped with the path 235 + to the client system-options file "dsm.sys". 236 + This option is to provide the effective derivation 237 + for other modules that want to call TSM executables. 238 + ''; 239 + }; 240 + }; 241 + 242 + cfg = config.programs.tsmClient; 243 + 244 + assertions = [ 245 + { 246 + assertion = checkIUnique (mapAttrsToList (k: v: v.name) cfg.servers); 247 + message = '' 248 + TSM servernames contain duplicate name 249 + (note that case doesn't matter!) 250 + ''; 251 + } 252 + { 253 + assertion = (cfg.defaultServername!=null)->(hasAttr cfg.defaultServername cfg.servers); 254 + message = "TSM defaultServername not found in list of servers"; 255 + } 256 + ]; 257 + 258 + dsmSysText = '' 259 + **** IBM Spectrum Protect (Tivoli Storage Manager) 260 + **** client system-options file "dsm.sys". 261 + **** Do not edit! 262 + **** This file is generated by NixOS configuration. 263 + 264 + ${optionalString (cfg.defaultServername!=null) "defaultserver ${cfg.defaultServername}"} 265 + 266 + ${concatStringsSep "\n" (mapAttrsToList (k: v: v.stanza) cfg.servers)} 267 + ''; 268 + 269 + in 270 + 271 + { 272 + 273 + inherit options; 274 + 275 + config = mkIf cfg.enable { 276 + inherit assertions; 277 + programs.tsmClient.dsmSysText = dsmSysText; 278 + programs.tsmClient.wrappedPackage = cfg.package.override rec { 279 + dsmSysCli = pkgs.writeText "dsm.sys" cfg.dsmSysText; 280 + dsmSysApi = dsmSysCli; 281 + }; 282 + environment.systemPackages = [ cfg.wrappedPackage ]; 283 + }; 284 + 285 + meta.maintainers = [ lib.maintainers.yarny ]; 286 + 287 + }
+106
nixos/modules/services/backup/tsm.nix
··· 1 + { config, lib, ... }: 2 + 3 + let 4 + 5 + inherit (lib.attrsets) hasAttr; 6 + inherit (lib.modules) mkDefault mkIf; 7 + inherit (lib.options) mkEnableOption mkOption; 8 + inherit (lib.types) nullOr strMatching; 9 + 10 + options.services.tsmBackup = { 11 + enable = mkEnableOption '' 12 + automatic backups with the 13 + IBM Spectrum Protect (Tivoli Storage Manager, TSM) client. 14 + This also enables 15 + <option>programs.tsmClient.enable</option> 16 + ''; 17 + command = mkOption { 18 + type = strMatching ".+"; 19 + default = "backup"; 20 + example = "incr"; 21 + description = '' 22 + The actual command passed to the 23 + <literal>dsmc</literal> executable to start the backup. 24 + ''; 25 + }; 26 + servername = mkOption { 27 + type = strMatching ".+"; 28 + example = "mainTsmServer"; 29 + description = '' 30 + Create a systemd system service 31 + <literal>tsm-backup.service</literal> that starts 32 + a backup based on the given servername's stanza. 33 + Note that this server's 34 + <option>passwdDir</option> will default to 35 + <filename>/var/lib/tsm-backup/password</filename> 36 + (but may be overridden); 37 + also, the service will use 38 + <filename>/var/lib/tsm-backup</filename> as 39 + <literal>HOME</literal> when calling 40 + <literal>dsmc</literal>. 41 + ''; 42 + }; 43 + autoTime = mkOption { 44 + type = nullOr (strMatching ".+"); 45 + default = null; 46 + example = "12:00"; 47 + description = '' 48 + The backup service will be invoked 49 + automatically at the given date/time, 50 + which must be in the format described in 51 + <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>5</manvolnum></citerefentry>. 52 + The default <literal>null</literal> 53 + disables automatic backups. 54 + ''; 55 + }; 56 + }; 57 + 58 + cfg = config.services.tsmBackup; 59 + cfgPrg = config.programs.tsmClient; 60 + 61 + assertions = [ 62 + { 63 + assertion = hasAttr cfg.servername cfgPrg.servers; 64 + message = "TSM service servername not found in list of servers"; 65 + } 66 + { 67 + assertion = cfgPrg.servers.${cfg.servername}.genPasswd; 68 + message = "TSM service requires automatic password generation"; 69 + } 70 + ]; 71 + 72 + in 73 + 74 + { 75 + 76 + inherit options; 77 + 78 + config = mkIf cfg.enable { 79 + inherit assertions; 80 + programs.tsmClient.enable = true; 81 + programs.tsmClient.servers."${cfg.servername}".passwdDir = 82 + mkDefault "/var/lib/tsm-backup/password"; 83 + systemd.services.tsm-backup = { 84 + description = "IBM Spectrum Protect (Tivoli Storage Manager) Backup"; 85 + # DSM_LOG needs a trailing slash to have it treated as a directory. 86 + # `/var/log` would be littered with TSM log files otherwise. 87 + environment.DSM_LOG = "/var/log/tsm-backup/"; 88 + # TSM needs a HOME dir to store certificates. 89 + environment.HOME = "/var/lib/tsm-backup"; 90 + # for exit status description see 91 + # https://www.ibm.com/support/knowledgecenter/en/SSEQVQ_8.1.8/client/c_sched_rtncode.html 92 + serviceConfig.SuccessExitStatus = "4 8"; 93 + # The `-se` option must come after the command. 94 + # The `-optfile` option suppresses a `dsm.opt`-not-found warning. 95 + serviceConfig.ExecStart = 96 + "${cfgPrg.wrappedPackage}/bin/dsmc ${cfg.command} -se='${cfg.servername}' -optfile=/dev/null"; 97 + serviceConfig.LogsDirectory = "tsm-backup"; 98 + serviceConfig.StateDirectory = "tsm-backup"; 99 + serviceConfig.StateDirectoryMode = "0750"; 100 + startAt = mkIf (cfg.autoTime!=null) cfg.autoTime; 101 + }; 102 + }; 103 + 104 + meta.maintainers = [ lib.maintainers.yarny ]; 105 + 106 + }
+165
pkgs/tools/backup/tsm-client/default.nix
··· 1 + { lib 2 + , stdenv 3 + , autoPatchelfHook 4 + , buildEnv 5 + , fetchurl 6 + , makeWrapper 7 + , procps 8 + , zlib 9 + # optional packages that enable certain features 10 + , acl ? null # EXT2/EXT3/XFS ACL support 11 + , jdk8 ? null # Java GUI 12 + , lvm2 ? null # LVM image backup and restore functions 13 + # path to `dsm.sys` configuration files 14 + , dsmSysCli ? "/etc/tsm-client/cli.dsm.sys" 15 + , dsmSysApi ? "/etc/tsm-client/api.dsm.sys" 16 + }: 17 + 18 + 19 + # For an explanation of optional packages 20 + # (features provided by them, version limits), see 21 + # https://www-01.ibm.com/support/docview.wss?uid=swg21052223#Version%208.1 22 + 23 + 24 + # IBM Tivoli Storage Manager Client uses a system-wide 25 + # client system-options file `dsm.sys` and expects it 26 + # to be located in a directory within the package. 27 + # Note that the command line client and the API use 28 + # different "dms.sys" files (located in different directories). 29 + # Since these files contain settings to be altered by the 30 + # admin user (e.g. TSM server name), we create symlinks 31 + # in place of the files that the client attempts to open. 32 + # Use the arguments `dsmSysCli` and `dsmSysApi` to 33 + # provide the location of the configuration files for 34 + # the command-line interface and the API, respectively. 35 + # 36 + # While the command-line interface contains wrappers 37 + # that help the executables find the configuration file, 38 + # packages that link against the API have to 39 + # set the environment variable `DSMI_DIR` to 40 + # point to this derivations `/dsmi_dir` directory symlink. 41 + # Other environment variables might be necessary, 42 + # depending on local configuration or usage; see: 43 + # https://www.ibm.com/support/knowledgecenter/en/SSEQVQ_8.1.8/client/c_cfg_sapiunix.html 44 + 45 + 46 + # The newest version of TSM client should be discoverable 47 + # by going the the `downloadPage` (see `meta` below), 48 + # there to "Client Latest Downloads", 49 + # "IBM Spectrum Protect Client Downloads and READMEs", 50 + # then to "Linux x86_64 Ubuntu client" (as of 2019-07-15). 51 + 52 + 53 + let 54 + 55 + meta = { 56 + homepage = https://www.ibm.com/us-en/marketplace/data-protection-and-recovery; 57 + downloadPage = https://www-01.ibm.com/support/docview.wss?uid=swg21239415; 58 + platforms = [ "x86_64-linux" ]; 59 + license = lib.licenses.unfree; 60 + maintainers = [ lib.maintainers.yarny ]; 61 + description = "IBM Spectrum Protect (Tivoli Storage Manager) CLI and API"; 62 + longDescription = '' 63 + IBM Spectrum Protect (Tivoli Storage Manager) provides 64 + a single point of control for backup and recovery. 65 + This package contains the client software, that is, 66 + a command line client and linkable libraries. 67 + 68 + Note that the software requires a system-wide 69 + client system-options file (commonly named "dsm.sys"). 70 + This package allows to use separate files for 71 + the command-line interface and for the linkable API. 72 + The location of those files can 73 + be provided as build parameters. 74 + ''; 75 + }; 76 + 77 + unwrapped = stdenv.mkDerivation rec { 78 + name = "tsm-client-${version}-unwrapped"; 79 + version = "8.1.8.0"; 80 + src = fetchurl { 81 + url = "ftp://public.dhe.ibm.com/storage/tivoli-storage-management/maintenance/client/v8r1/Linux/LinuxX86_DEB/BA/v818/${version}-TIV-TSMBAC-LinuxX86_DEB.tar"; 82 + sha256 = "0c1d0jm0i7qjd314nhj2vj8fs7sncm1x2n4d6dg4049jniyvjhpk"; 83 + }; 84 + inherit meta; 85 + 86 + nativeBuildInputs = [ 87 + autoPatchelfHook 88 + ]; 89 + buildInputs = [ 90 + stdenv.cc.cc 91 + zlib 92 + ]; 93 + runtimeDependencies = [ 94 + lvm2 95 + ]; 96 + sourceRoot = "."; 97 + 98 + postUnpack = '' 99 + for debfile in *.deb 100 + do 101 + ar -x "$debfile" 102 + tar --xz --extract --file=data.tar.xz 103 + rm data.tar.xz 104 + done 105 + ''; 106 + 107 + installPhase = '' 108 + runHook preInstall 109 + mkdir --parents $out 110 + mv --target-directory=$out usr/* opt 111 + runHook postInstall 112 + ''; 113 + 114 + # Fix relative symlinks after `/usr` was moved up one level 115 + preFixup = '' 116 + for link in $out/lib/* $out/bin/* 117 + do 118 + target=$(readlink "$link") 119 + if [ "$(cut -b -6 <<< "$target")" != "../../" ] 120 + then 121 + echo "cannot fix this symlink: $link -> $target" 122 + exit 1 123 + fi 124 + ln --symbolic --force --no-target-directory "$out/$(cut -b 7- <<< "$target")" "$link" 125 + done 126 + ''; 127 + }; 128 + 129 + in 130 + 131 + buildEnv { 132 + name = "tsm-client-${unwrapped.version}"; 133 + inherit meta; 134 + passthru = { inherit unwrapped; }; 135 + paths = [ unwrapped ]; 136 + buildInputs = [ makeWrapper ]; 137 + pathsToLink = [ 138 + "/" 139 + "/bin" 140 + "/opt/tivoli/tsm/client/ba/bin" 141 + "/opt/tivoli/tsm/client/api/bin64" 142 + ]; 143 + # * Provide top-level symlinks `dsm_dir` and `dsmi_dir` 144 + # to the so-called "installation directories" 145 + # * Add symlinks to the "installation directories" 146 + # that point to the `dsm.sys` configuration files 147 + # * Drop the Java GUI executable unless `jdk` is present 148 + # * Create wrappers for the command-line interface to 149 + # prepare `PATH` and `DSM_DIR` environment variables 150 + postBuild = '' 151 + ln --symbolic --no-target-directory opt/tivoli/tsm/client/ba/bin $out/dsm_dir 152 + ln --symbolic --no-target-directory opt/tivoli/tsm/client/api/bin64 $out/dsmi_dir 153 + ln --symbolic --no-target-directory "${dsmSysCli}" $out/dsm_dir/dsm.sys 154 + ln --symbolic --no-target-directory "${dsmSysApi}" $out/dsmi_dir/dsm.sys 155 + ${lib.strings.optionalString (jdk8==null) "rm $out/bin/dsmj"} 156 + for bin in $out/bin/* 157 + do 158 + target=$(readlink "$bin") 159 + rm "$bin" 160 + makeWrapper "$target" "$bin" \ 161 + --prefix PATH : "$out/dsm_dir:${lib.strings.makeBinPath [ procps acl jdk8 ]}" \ 162 + --set DSM_DIR $out/dsm_dir 163 + done 164 + ''; 165 + }
+3
pkgs/top-level/all-packages.nix
··· 2682 2682 2683 2683 teamocil = callPackage ../tools/misc/teamocil { }; 2684 2684 2685 + tsm-client = callPackage ../tools/backup/tsm-client { jdk8 = null; }; 2686 + tsm-client-withGui = callPackage ../tools/backup/tsm-client { }; 2687 + 2685 2688 tridactyl-native = callPackage ../tools/networking/tridactyl-native { }; 2686 2689 2687 2690 trompeloeil = callPackage ../development/libraries/trompeloeil { };