diff --git a/conf/clush.conf b/conf/clush.conf index 6e19b013..753d38e0 100644 --- a/conf/clush.conf +++ b/conf/clush.conf @@ -10,6 +10,7 @@ command_timeout: 0 color: auto fd_max: 8192 history_size: 100 +maxrc: no node_count: yes verbosity: 1 diff --git a/doc/extras/vim/syntax/clushconf.vim b/doc/extras/vim/syntax/clushconf.vim index 5ad89a8b..3e157169 100644 --- a/doc/extras/vim/syntax/clushconf.vim +++ b/doc/extras/vim/syntax/clushconf.vim @@ -16,7 +16,7 @@ syn match clushComment "#.*$" syn match clushComment ";.*$" syn match clushHeader "\[\w\+\]" -syn keyword clushKeys fanout command_timeout connect_timeout color fd_max history_size node_count verbosity +syn keyword clushKeys fanout command_timeout connect_timeout color fd_max history_size node_count maxrc verbosity syn keyword clushKeys ssh_user ssh_path ssh_options syn keyword clushKeys rsh_path rcp_path rcp_options diff --git a/doc/man/man1/clush.1 b/doc/man/man1/clush.1 index 8fd94f56..660057bf 100644 --- a/doc/man/man1/clush.1 +++ b/doc/man/man1/clush.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH CLUSH 1 "2019-12-01" "1.8.3" "ClusterShell User Manual" +.TH CLUSH 1 "2021-10-27" "1.8.3" "ClusterShell User Manual" .SH NAME clush \- execute shell commands on a cluster . @@ -234,7 +234,7 @@ like \-b but including standard error .B \-r\fP,\fB \-\-regroup fold nodeset using node groups .TP -.B \-S +.B \-S\fP,\fB \-\-maxrc return the largest of command return codes .TP .BI \-\-color\fB= WHENCOLOR diff --git a/doc/man/man5/clush.conf.5 b/doc/man/man5/clush.conf.5 index 68bcdaf9..4657ca3c 100644 --- a/doc/man/man5/clush.conf.5 +++ b/doc/man/man5/clush.conf.5 @@ -103,6 +103,9 @@ list. Negative values imply unlimited history file size. Should \fBclush\fP display additional (node count) information in buffer header? (\fIyes\fP/\fIno\fP) .TP +.B maxrc +Should \fBclush\fP return the largest of command return codes? (\fIyes\fP/\fIno\fP) +.TP .B verbosity Set the verbosity level: \fI0\fP (quiet), \fI1\fP (default), \fI2\fP (verbose) or more (debug). @@ -146,6 +149,7 @@ history_size: 100 color: auto fd_max: 10240 node_count: yes +maxrc: no .fi .sp diff --git a/doc/sphinx/config.rst b/doc/sphinx/config.rst index 135021ba..1320d48f 100644 --- a/doc/sphinx/config.rst +++ b/doc/sphinx/config.rst @@ -64,6 +64,13 @@ The following table describes available *clush* config file settings. | node_count | Should *clush* display additional (node count) | | | information in buffer header? (yes/no) | +-----------------+----------------------------------------------------+ +| maxrc | Should *clush* return the largest of command | +| | return codes? (yes/no) | +| | If set to no (the default), *clush* exit status | +| | gives no information about command return codes, | +| | but rather reports on *clush* execution itself | +| | (zero indicating a successful run). | ++-----------------+----------------------------------------------------+ | verbosity | Set the verbosity level: 0 (quiet), 1 (default), | | | 2 (verbose) or more (debug). | +-----------------+----------------------------------------------------+ diff --git a/doc/txt/clush.conf.txt b/doc/txt/clush.conf.txt index 684542a6..81f4a24c 100644 --- a/doc/txt/clush.conf.txt +++ b/doc/txt/clush.conf.txt @@ -71,6 +71,8 @@ history_size node_count Should ``clush`` display additional (node count) information in buffer header? (`yes`/`no`) +maxrc + Should ``clush`` return the largest of command return codes? (yes/no) verbosity Set the verbosity level: `0` (quiet), `1` (default), `2` (verbose) or more (debug). @@ -108,6 +110,7 @@ Simple configuration file. | history_size: 100 | color: auto | fd_max: 10240 +| maxrc: no | node_count: yes | diff --git a/doc/txt/clush.txt b/doc/txt/clush.txt index 7c070cee..709ce1a3 100644 --- a/doc/txt/clush.txt +++ b/doc/txt/clush.txt @@ -7,7 +7,7 @@ execute shell commands on a cluster ----------------------------------- :Author: Stephane Thiell -:Date: 2019-12-01 +:Date: 2021-10-27 :Copyright: GNU Lesser General Public License version 2.1 or later (LGPLv2.1+) :Version: 1.8.3 :Manual section: 1 @@ -159,7 +159,7 @@ Output behaviour: -b, --dshbak display gathered results in a dshbak-like way (note: it will only try to aggregate the output of commands with same return codes) -B like -b but including standard error -r, --regroup fold nodeset using node groups - -S return the largest of command return codes + -S, --maxrc return the largest of command return codes --color=WHENCOLOR whether to use ANSI colors to surround node or nodeset prefix/header with escape sequences to display them in color on the terminal. *WHENCOLOR* is ``never``, ``always`` or ``auto`` (which use color if standard output/error refer to a terminal). Colors are set to [34m (blue foreground text) for stdout and [31m (red foreground text) for stderr, and cannot be modified. --diff show diff between common outputs (find the best reference output by focusing on largest nodeset and also smaller command return code) @@ -183,7 +183,7 @@ Connection options: -u COMMAND_TIMEOUT, --command_timeout=COMMAND_TIMEOUT limit time for command to run on the node -R WORKER, --worker=WORKER - worker name to use for connection (``exec``, ``ssh``, ``rsh``, ``pdsh``), default is ``ssh`` + worker name to use for connection (``exec``, ``ssh``, ``rsh``, ``pdsh``, or the name of a Python worker module), default is ``ssh`` --remote=REMOTE whether to enable remote execution: in tree mode, 'yes' forces connections to the leaf nodes for execution, 'no' establishes connections up to the leaf parent nodes for execution (default is 'yes') For a short explanation of these options, see ``-h, --help``. diff --git a/lib/ClusterShell/CLI/Clush.py b/lib/ClusterShell/CLI/Clush.py index 0e843fef..e88a89a5 100755 --- a/lib/ClusterShell/CLI/Clush.py +++ b/lib/ClusterShell/CLI/Clush.py @@ -1095,7 +1095,7 @@ def main(): clush_exit(1, task) rc = 0 - if options.maxrc: + if config.maxrc: # Instead of clush return code, return commands retcode rc = task.max_retcode() if task.num_timeout() > 0: diff --git a/lib/ClusterShell/CLI/Config.py b/lib/ClusterShell/CLI/Config.py index 933d6eae..4cf30fd0 100644 --- a/lib/ClusterShell/CLI/Config.py +++ b/lib/ClusterShell/CLI/Config.py @@ -56,6 +56,7 @@ class ClushConfig(configparser.ConfigParser, object): "color": THREE_CHOICES[-1], # auto "verbosity": "%d" % VERB_STD, "node_count": "yes", + "maxrc": "no", "fd_max": "8192"} def __init__(self, options, filename=None): @@ -94,6 +95,8 @@ def __init__(self, options, filename=None): self._set_main("command_timeout", options.command_timeout) if options.whencolor: self._set_main("color", options.whencolor) + if options.maxrc: + self._set_main("maxrc", options.maxrc) try: # -O/--option KEY=VALUE @@ -212,6 +215,11 @@ def node_count(self): """node_count value as a boolean""" return self.getboolean("Main", "node_count") + @property + def maxrc(self): + """maxrc value as a boolean""" + return self.getboolean("Main", "maxrc") + @property def fd_max(self): """max number of open files (soft rlimit)""" diff --git a/lib/ClusterShell/CLI/OptionParser.py b/lib/ClusterShell/CLI/OptionParser.py index 449d6264..e47e6cea 100644 --- a/lib/ClusterShell/CLI/OptionParser.py +++ b/lib/ClusterShell/CLI/OptionParser.py @@ -172,7 +172,7 @@ def install_display_options(self, help="node / line content separator string " "(default: ':')") else: - optgrp.add_option("-S", action="store_true", dest="maxrc", + optgrp.add_option("-S", "--maxrc", action="store_true", dest="maxrc", help="return the largest of command return codes") if msgtree_mode: diff --git a/tests/CLIClushTest.py b/tests/CLIClushTest.py index 818802c4..02c9fb77 100644 --- a/tests/CLIClushTest.py +++ b/tests/CLIClushTest.py @@ -275,6 +275,14 @@ def test_017_retcodes(self): self._clush_t(["-w", HOSTNAME, "/bin/false"], None, b"", 0, exp_err) self._clush_t(["-w", HOSTNAME, "-b", "/bin/false"], None, b"", 0, exp_err) self._clush_t(["-S", "-w", HOSTNAME, "/bin/false"], None, b"", 1, exp_err) + self._clush_t(["--maxrc", "-w", HOSTNAME, "/bin/false"], None, b"", 1, exp_err) + self._clush_t(["-O", "maxrc=yes", "-w", HOSTNAME, "/bin/false"], None, + b"", 1, exp_err) + # -O takes precedence over --maxrc + self._clush_t(["--maxrc", "-O", "maxrc=no", "-w", HOSTNAME, "/bin/false"], None, + b"", 0, exp_err) + self._clush_t(["-O", "maxrc=no", "--maxrc", "-w", HOSTNAME, "/bin/false"], None, + b"", 0, exp_err) for i in (1, 2, 127, 128, 255): s = "clush: %s: exited with exit code %d\n" % (HOSTNAME, i) self._clush_t(["-S", "-w", HOSTNAME, "exit %d" % i], None, b"", i, diff --git a/tests/CLIConfigTest.py b/tests/CLIConfigTest.py index 072e4862..0b27665d 100644 --- a/tests/CLIConfigTest.py +++ b/tests/CLIConfigTest.py @@ -38,6 +38,7 @@ def testClushConfigEmpty(self): self.assertEqual(config.color, WHENCOLOR_CHOICES[-1]) self.assertEqual(config.verbosity, VERB_STD) self.assertEqual(config.fanout, 64) + self.assertEqual(config.maxrc, False) self.assertEqual(config.node_count, True) self.assertEqual(config.connect_timeout, 10) self.assertEqual(config.command_timeout, 0) @@ -60,6 +61,7 @@ def testClushConfigAlmostEmpty(self): config = ClushConfig(options, filename=f.name) self.assertEqual(config.color, WHENCOLOR_CHOICES[-1]) self.assertEqual(config.verbosity, VERB_STD) + self.assertEqual(config.maxrc, False) self.assertEqual(config.node_count, True) self.assertEqual(config.fanout, 64) self.assertEqual(config.connect_timeout, 10) @@ -96,6 +98,7 @@ def testClushConfigDefault(self): display.vprint(VERB_DEBUG, "shouldn't see this") self.assertEqual(config.color, WHENCOLOR_CHOICES[2]) self.assertEqual(config.verbosity, VERB_STD) + self.assertEqual(config.maxrc, False) self.assertEqual(config.node_count, True) self.assertEqual(config.fanout, 42) self.assertEqual(config.connect_timeout, 14) @@ -116,6 +119,7 @@ def testClushConfigFull(self): command_timeout: 0 history_size: 100 color: auto + maxrc: yes node_count: yes verbosity: 1 ssh_user: root @@ -132,6 +136,7 @@ def testClushConfigFull(self): config = ClushConfig(options, filename=f.name) self.assertEqual(config.color, WHENCOLOR_CHOICES[2]) self.assertEqual(config.verbosity, VERB_STD) + self.assertEqual(config.maxrc, True) self.assertEqual(config.node_count, True) self.assertEqual(config.fanout, 42) self.assertEqual(config.connect_timeout, 14)