git.iko.soy/typeable/nixpkgs
on master help sign in
Merge pull request #138386 from Yarny0/tsm-client
commit5c4fa6964f76f32093d4ff93c66c1e57dbe86e22 authorSandro <sandro.jaeckel@gmail.com> dateTue 18 Jan 2022, 20:50 (5 years ago) parent66023ae756f453
7 files changed+231−56
nixos/modules/programs/tsm-client.nix+4−4 nixos/modules/services/backup/tsm.nix+33−14 nixos/tests/all-tests.nix+1 nixos/tests/tsm-client-gui.nix+57 pkgs/tools/backup/tsm-client/default.nix+76−36 pkgs/tools/backup/tsm-client/test-cli.nix+58 pkgs/top-level/all-packages.nix+2−2
nixos/modules/programs/tsm-client.nix+4−4
@@ -7,7 +7,7 @@ let
77 inherit (lib.modules) mkDefault mkIf;
88 inherit (lib.options) literalExpression mkEnableOption mkOption;
99 inherit (lib.strings) concatStringsSep optionalString toLower;
10− inherit (lib.types) addCheck attrsOf lines nullOr package path port str strMatching submodule;
10+ inherit (lib.types) addCheck attrsOf lines nonEmptyStr nullOr package path port str strMatching submodule;
1111
1212 # Checks if given list of strings contains unique
1313 # elements when compared without considering case.
@@ -35,7 +35,7 @@ let
3535 '';
3636 };
3737 options.server = mkOption {
38− type = strMatching ".+";
38+ type = nonEmptyStr;
3939 example = "tsmserver.company.com";
4040 description = ''
4141 Host/domain name or IP address of the IBM TSM server.
@@ -56,7 +56,7 @@ let
5656 '';
5757 };
5858 options.node = mkOption {
59− type = strMatching ".+";
59+ type = nonEmptyStr;
6060 example = "MY-TSM-NODE";
6161 description = ''
6262 Target node name on the IBM TSM server.
@@ -144,7 +144,7 @@ let
144144 };
145145 config.name = mkDefault name;
146146 # Client system-options file directives are explained here:
147− # https://www.ibm.com/support/knowledgecenter/SSEQVQ_8.1.8/client/c_opt_usingopts.html
147+ # https://www.ibm.com/docs/en/spectrum-protect/8.1.13?topic=commands-processing-options
148148 config.extraConfig =
149149 mapAttrs (lib.trivial.const mkDefault) (
150150 {
nixos/modules/services/backup/tsm.nix+33−14
@@ -5,7 +5,7 @@ let
55 inherit (lib.attrsets) hasAttr;
66 inherit (lib.modules) mkDefault mkIf;
77 inherit (lib.options) mkEnableOption mkOption;
8− inherit (lib.types) nullOr strMatching;
8+ inherit (lib.types) nonEmptyStr nullOr;
99
1010 options.services.tsmBackup = {
1111 enable = mkEnableOption ''
@@ -15,7 +15,7 @@ let
1515 <option>programs.tsmClient.enable</option>
1616 '';
1717 command = mkOption {
18− type = strMatching ".+";
18+ type = nonEmptyStr;
1919 default = "backup";
2020 example = "incr";
2121 description = ''
@@ -24,7 +24,7 @@ let
2424 '';
2525 };
2626 servername = mkOption {
27− type = strMatching ".+";
27+ type = nonEmptyStr;
2828 example = "mainTsmServer";
2929 description = ''
3030 Create a systemd system service
@@ -41,7 +41,7 @@ let
4141 '';
4242 };
4343 autoTime = mkOption {
44− type = nullOr (strMatching ".+");
44+ type = nullOr nonEmptyStr;
4545 default = null;
4646 example = "12:00";
4747 description = ''
@@ -87,16 +87,35 @@ in
8787 environment.DSM_LOG = "/var/log/tsm-backup/";
8888 # TSM needs a HOME dir to store certificates.
8989 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";
90+ serviceConfig = {
91+ # for exit status description see
92+ # https://www.ibm.com/docs/en/spectrum-protect/8.1.13?topic=clients-client-return-codes
93+ SuccessExitStatus = "4 8";
94+ # The `-se` option must come after the command.
95+ # The `-optfile` option suppresses a `dsm.opt`-not-found warning.
96+ ExecStart =
97+ "${cfgPrg.wrappedPackage}/bin/dsmc ${cfg.command} -se='${cfg.servername}' -optfile=/dev/null";
98+ LogsDirectory = "tsm-backup";
99+ StateDirectory = "tsm-backup";
100+ StateDirectoryMode = "0750";
101+ # systemd sandboxing
102+ LockPersonality = true;
103+ NoNewPrivileges = true;
104+ PrivateDevices = true;
105+ #PrivateTmp = true; # would break backup of {/var,}/tmp
106+ #PrivateUsers = true; # would block backup of /home/*
107+ ProtectClock = true;
108+ ProtectControlGroups = true;
109+ ProtectHome = "read-only";
110+ ProtectHostname = true;
111+ ProtectKernelLogs = true;
112+ ProtectKernelModules = true;
113+ ProtectKernelTunables = true;
114+ ProtectProc = "noaccess";
115+ ProtectSystem = "strict";
116+ RestrictNamespaces = true;
117+ RestrictSUIDSGID = true;
118+ };
100119 startAt = mkIf (cfg.autoTime!=null) cfg.autoTime;
101120 };
102121 };
nixos/tests/all-tests.nix+1
@@ -490,6 +490,7 @@ in
490490 trezord = handleTest ./trezord.nix {};
491491 trickster = handleTest ./trickster.nix {};
492492 trilium-server = handleTestOn ["x86_64-linux"] ./trilium-server.nix {};
493+ tsm-client-gui = handleTest ./tsm-client-gui.nix {};
493494 txredisapi = handleTest ./txredisapi.nix {};
494495 tuptime = handleTest ./tuptime.nix {};
495496 turbovnc-headless-server = handleTest ./turbovnc-headless-server.nix {};
nixos/tests/tsm-client-gui.nixnew file+57
@@ -0,0 +1,57 @@
1+# The tsm-client GUI first tries to connect to a server.
2+# We can't simulate a server, so we just check if
3+# it reports the correct connection failure error.
4+# After that the test persuades the GUI
5+# to show its main application window
6+# and verifies some configuration information.
7+
8+import ./make-test-python.nix ({ lib, pkgs, ... }: {
9+ name = "tsm-client";
10+
11+ enableOCR = true;
12+
13+ machine = { pkgs, ... }: {
14+ imports = [ ./common/x11.nix ];
15+ programs.tsmClient = {
16+ enable = true;
17+ package = pkgs.tsm-client-withGui;
18+ defaultServername = "testserver";
19+ servers.testserver = {
20+ # 192.0.0.8 is a "dummy address" according to RFC 7600
21+ server = "192.0.0.8";
22+ node = "SOME-NODE";
23+ passwdDir = "/tmp";
24+ };
25+ };
26+ };
27+
28+ testScript = ''
29+ machine.succeed("which dsmj") # fail early if this is missing
30+ machine.wait_for_x()
31+ machine.execute("DSM_LOG=/tmp dsmj -optfile=/dev/null >&2 &")
32+
33+ # does it report the "TCP/IP connection failure" error code?
34+ machine.wait_for_window("IBM Spectrum Protect")
35+ machine.wait_for_text("ANS2610S")
36+ machine.send_key("esc")
37+
38+ # it asks to continue to restore a local backupset now;
39+ # "yes" (return) leads to the main application window
40+ machine.wait_for_text("backupset")
41+ machine.send_key("ret")
42+
43+ # main window: navigate to "Connection Information"
44+ machine.wait_for_text("Welcome")
45+ machine.send_key("alt-f") # "File" menu
46+ machine.send_key("c") # "Connection Information"
47+
48+ # "Connection Information" dialog box
49+ machine.wait_for_window("Connection Information")
50+ machine.wait_for_text("SOME-NODE")
51+ machine.wait_for_text("${pkgs.tsm-client.passthru.unwrapped.version}")
52+
53+ machine.shutdown()
54+ '';
55+
56+ meta.maintainers = [ lib.maintainers.yarny ];
57+})
pkgs/tools/backup/tsm-client/default.nix+76−36
@@ -1,15 +1,20 @@
11{ lib
2+, callPackage
3+, nixosTests
24, stdenv
5+, fetchurl
36, autoPatchelfHook
7+, rpmextract
8+, openssl
9+, zlib
10+, lvm2 # LVM image backup and restore functions (optional)
11+, acl # EXT2/EXT3/XFS ACL support (optional)
12+, gnugrep
13+, procps
14+, jdk8 # Java GUI (needed for `enableGui`)
415, buildEnv
5−, fetchurl
616, 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
17+, enableGui ? false # enables Java GUI `dsmj`
1318# path to `dsm.sys` configuration files
1419, dsmSysCli ? "/etc/tsm-client/cli.dsm.sys"
1520, dsmSysApi ? "/etc/tsm-client/api.dsm.sys"
@@ -18,7 +23,7 @@
1823
1924# For an explanation of optional packages
2025# (features provided by them, version limits), see
21−# https://www-01.ibm.com/support/docview.wss?uid=swg21052223#Version%208.1
26+# https://www.ibm.com/support/pages/node/660813#Version%208.1
2227
2328
2429# IBM Tivoli Storage Manager Client uses a system-wide
@@ -40,22 +45,33 @@
4045# point to this derivations `/dsmi_dir` directory symlink.
4146# Other environment variables might be necessary,
4247# 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).
48+# https://www.ibm.com/docs/en/spectrum-protect/8.1.13?topic=solaris-set-api-environment-variables
49+
50+
51+# The newest version of TSM client should be discoverable by
52+# going to the `downloadPage` (see `meta` below).
53+# Find the "Backup-archive client" table on that page.
54+# Look for "Download Documents" of the latest release.
55+# Here, two links must be checked:
56+# * "IBM Spectrum Protect Client ... Downloads and READMEs":
57+# In the table at the page's bottom,
58+# check the date of the "Linux x86_64 client"
59+# * "IBM Spectrum Protect BA client ... interim fix downloads"
60+# Look for the "Linux x86_64 client" rows
61+# in the table # at the bottom of each page.
62+# Follow the "HTTPS" link of the row with the latest date stamp.
63+# In the directory listing to show up, pick the big `.tar` file.
64+#
65+# (as of 2021-12-18)
5166
5267
5368let
5469
5570 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";
71+ homepage = "https://www.ibm.com/products/data-protection-and-recovery";
72+ downloadPage = "https://www.ibm.com/support/pages/ibm-spectrum-protect-downloads-latest-fix-packs-and-interim-fixes";
5873 platforms = [ "x86_64-linux" ];
74+ mainProgram = "dsmc";
5975 license = lib.licenses.unfree;
6076 maintainers = [ lib.maintainers.yarny ];
6177 description = "IBM Spectrum Protect (Tivoli Storage Manager) CLI and API";
@@ -74,34 +90,53 @@ let
7490 '';
7591 };
7692
93+ passthru.tests = {
94+ test-cli = callPackage ./test-cli.nix {};
95+ test-gui = nixosTests.tsm-client-gui;
96+ };
97+
98+ mkSrcUrl = version:
99+ let
100+ major = lib.versions.major version;
101+ minor = lib.versions.minor version;
102+ patch = lib.versions.patch version;
103+ fixup = lib.lists.elemAt (lib.versions.splitVersion version) 3;
104+ in
105+ "https://public.dhe.ibm.com/storage/tivoli-storage-management/${if fixup=="0" then "maintenance" else "patches"}/client/v${major}r${minor}/Linux/LinuxX86/BA/v${major}${minor}${patch}/${version}-TIV-TSMBAC-LinuxX86.tar";
106+
77107 unwrapped = stdenv.mkDerivation rec {
78108 name = "tsm-client-${version}-unwrapped";
79− version = "8.1.8.0";
109+ version = "8.1.13.3";
80110 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";
111+ url = mkSrcUrl version;
112+ sha256 = "1dwczf236drdaf4jcfzz5154vdwvxf5zraxhrhiddl6n80hnvbcd";
83113 };
84− inherit meta;
114+ inherit meta passthru;
85115
86116 nativeBuildInputs = [
87117 autoPatchelfHook
118+ rpmextract
88119 ];
89120 buildInputs = [
121+ openssl
90122 stdenv.cc.cc
91123 zlib
92124 ];
93125 runtimeDependencies = [
94− lvm2
126+ (lib.attrsets.getLib lvm2)
95127 ];
96128 sourceRoot = ".";
97129
98130 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
131+ rpmextract TIVsm-API64.x86_64.rpm
132+ rpmextract TIVsm-APIcit.x86_64.rpm
133+ rpmextract TIVsm-BA.x86_64.rpm
134+ rpmextract TIVsm-BAcit.x86_64.rpm
135+ rpmextract TIVsm-BAhdw.x86_64.rpm
136+ rpmextract TIVsm-JBB.x86_64.rpm
137+ # use globbing so that version updates don't break the build:
138+ rpmextract gskcrypt64-*.linux.x86_64.rpm
139+ rpmextract gskssl64-*.linux.x86_64.rpm
105140 '';
106141
107142 installPhase = ''
@@ -113,7 +148,7 @@ let
113148
114149 # Fix relative symlinks after `/usr` was moved up one level
115150 preFixup = ''
116− for link in $out/lib/* $out/bin/*
151+ for link in $out/lib{,64}/* $out/bin/*
117152 do
118153 target=$(readlink "$link")
119154 if [ "$(cut -b -6 <<< "$target")" != "../../" ]
@@ -126,14 +161,19 @@ let
126161 '';
127162 };
128163
164+ binPath = lib.makeBinPath ([ acl gnugrep procps ]
165+ ++ lib.optional enableGui jdk8);
166+
129167in
130168
131169buildEnv {
132170 name = "tsm-client-${unwrapped.version}";
133− inherit meta;
134− passthru = { inherit unwrapped; };
171+ meta = meta // lib.attrsets.optionalAttrs enableGui {
172+ mainProgram = "dsmj";
173+ };
174+ passthru = passthru // { inherit unwrapped; };
135175 paths = [ unwrapped ];
136− buildInputs = [ makeWrapper ];
176+ nativeBuildInputs = [ makeWrapper ];
137177 pathsToLink = [
138178 "/"
139179 "/bin"
@@ -144,7 +184,7 @@ buildEnv {
144184 # to the so-called "installation directories"
145185 # * Add symlinks to the "installation directories"
146186 # that point to the `dsm.sys` configuration files
147− # * Drop the Java GUI executable unless `jdk` is present
187+ # * Drop the Java GUI executable unless `enableGui` is set
148188 # * Create wrappers for the command-line interface to
149189 # prepare `PATH` and `DSM_DIR` environment variables
150190 postBuild = ''
@@ -152,13 +192,13 @@ buildEnv {
152192 ln --symbolic --no-target-directory opt/tivoli/tsm/client/api/bin64 $out/dsmi_dir
153193 ln --symbolic --no-target-directory "${dsmSysCli}" $out/dsm_dir/dsm.sys
154194 ln --symbolic --no-target-directory "${dsmSysApi}" $out/dsmi_dir/dsm.sys
155− ${lib.optionalString (jdk8==null) "rm $out/bin/dsmj"}
195+ ${lib.optionalString (!enableGui) "rm $out/bin/dsmj"}
156196 for bin in $out/bin/*
157197 do
158198 target=$(readlink "$bin")
159199 rm "$bin"
160200 makeWrapper "$target" "$bin" \
161− --prefix PATH : "$out/dsm_dir:${lib.strings.makeBinPath [ procps acl jdk8 ]}" \
201+ --prefix PATH : "$out/dsm_dir:${binPath}" \
162202 --set DSM_DIR $out/dsm_dir
163203 done
164204 '';
pkgs/tools/backup/tsm-client/test-cli.nixnew file+58
@@ -0,0 +1,58 @@
1+{ lib
2+, writeText
3+, runCommand
4+, tsm-client
5+}:
6+
7+# Let the client try to connect to a server.
8+# We can't simulate a server, so there's no more to test.
9+
10+let
11+
12+ # 192.0.0.8 is a "dummy address" according to RFC 7600
13+ dsmSysCli = writeText "cli.dsm.sys" ''
14+ defaultserver testserver
15+ server testserver
16+ commmethod v6tcpip
17+ tcpserveraddress 192.0.0.8
18+ nodename ARBITRARYNODENAME
19+ '';
20+
21+ tsm-client_ = tsm-client.override { inherit dsmSysCli; };
22+
23+ env.nativeBuildInputs = [ tsm-client_ ];
24+
25+ versionString =
26+ let
27+ inherit (tsm-client_.passthru.unwrapped) version;
28+ major = lib.versions.major version;
29+ minor = lib.versions.minor version;
30+ patch = lib.versions.patch version;
31+ fixup = lib.lists.elemAt (lib.versions.splitVersion version) 3;
32+ in
33+ "Client Version ${major}, Release ${minor}, Level ${patch}.${fixup}";
34+
35+in
36+
37+runCommand "${tsm-client.name}-test-cli" env ''
38+ set -o nounset
39+ set -o pipefail
40+
41+ export DSM_LOG=$(mktemp -d ./dsm_log.XXXXXXXXXXX)
42+
43+ { dsmc -optfile=/dev/null || true; } | tee dsmc-stdout
44+
45+ # does it report the correct version?
46+ grep --fixed-strings '${versionString}' dsmc-stdout
47+
48+ # does it use the provided dsm.sys config file?
49+ # if it does, it states the node's name
50+ grep ARBITRARYNODENAME dsmc-stdout
51+
52+ # does it try (and fail) to connect to the server?
53+ # if it does, it reports the "TCP/IP connection failure" error code
54+ grep ANS1017E dsmc-stdout
55+ grep ANS1017E $DSM_LOG/dsmerror.log
56+
57+ touch $out
58+''
pkgs/top-level/all-packages.nix+2−2
@@ -4927,8 +4927,8 @@ with pkgs;
49274927
49284928 timeline = callPackage ../applications/office/timeline { };
49294929
4930− tsm-client = callPackage ../tools/backup/tsm-client { jdk8 = null; };
4931− tsm-client-withGui = callPackage ../tools/backup/tsm-client { };
4930+ tsm-client = callPackage ../tools/backup/tsm-client { };
4931+ tsm-client-withGui = callPackage ../tools/backup/tsm-client { enableGui = true; };
49324932
49334933 tracker = callPackage ../development/libraries/tracker { };
49344934