Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package clustershell for openSUSE:Factory checked in at 2021-11-09 23:54:47 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/clustershell (Old) and /work/SRC/openSUSE:Factory/.clustershell.new.1890 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "clustershell" Tue Nov 9 23:54:47 2021 rev:7 rq:930223 version:1.8.4 Changes: -------- --- /work/SRC/openSUSE:Factory/clustershell/clustershell.changes 2020-09-09 18:08:51.263575713 +0200 +++ /work/SRC/openSUSE:Factory/.clustershell.new.1890/clustershell.changes 2021-11-09 23:55:14.063971701 +0100 @@ -1,0 +2,15 @@ +Mon Nov 8 17:42:04 UTC 2021 - Stephane Thiell <sthi...@stanford.edu> + +- Update to upstream release 1.8.4: + * RangeSetND: fix padding info when slicing using __getitem__() + * Defaults: Allow out-of-tree worker modules + * NodeUtils: allow YAML list to declare node groups + * Tree: Use default local_worker and allow overriding Defaults + * Worker/Rsh: return maxrc properly for Rsh Worker + * xCAT binding: add support for spaces in group names + * CLI/Clush: Avoid python3 error with no stdin + * CLI/Clush: use os.read() in stdin thread + * CLI/Clush: Add maxrc option to clush.conf + * CLI/Display: Add support for NO_COLOR and CLICOLOR + +------------------------------------------------------------------- Old: ---- ClusterShell-1.8.3.tar.gz New: ---- ClusterShell-1.8.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ clustershell.spec ++++++ --- /var/tmp/diff_new_pack.SWtu3I/_old 2021-11-09 23:55:14.551971949 +0100 +++ /var/tmp/diff_new_pack.SWtu3I/_new 2021-11-09 23:55:14.555971952 +0100 @@ -1,8 +1,8 @@ # # spec file for package clustershell # -# Copyright (c) 2020 SUSE LLC -# Copyright (c) 2017 Stephane Thiell <sthi...@stanford.edu> +# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2021 Stephane Thiell <sthi...@stanford.edu> # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -47,7 +47,7 @@ %global srcname ClusterShell Name: clustershell -Version: 1.8.3 +Version: 1.8.4 Release: 1%{?dist} Summary: Python framework for efficient cluster administration License: LGPL-2.1-or-later @@ -115,7 +115,6 @@ %description -n %{python3_pkgprefix}-%{name} ClusterShell Python 3 module and related command line tools. - %prep %setup -q -n %{srcname}-%{version} @@ -197,6 +196,7 @@ %{python3_sitelib}/ClusterShell-*-py?.?.egg-info %else + %files -n %{python3_pkgprefix}-%{name} %if 0%{?rhel} %defattr(-,root,root,-) ++++++ ClusterShell-1.8.3.tar.gz -> ClusterShell-1.8.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/ChangeLog new/ClusterShell-1.8.4/ChangeLog --- old/ClusterShell-1.8.3/ChangeLog 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/ChangeLog 2021-11-06 01:59:59.000000000 +0100 @@ -1,3 +1,27 @@ +2021-11-03 S. Thiell <sthi...@stanford.edu> + + * Version 1.8.4 released. The main changes are listed below. + + * RangeSetND: fix padding info when slicing using __getitem__() (#429) + + * Defaults: Allow out-of-tree worker modules + + * NodeUtils: allow YAML list to declare node groups (#438) + + * Tree: Use default local_worker and allow overriding Defaults + + * Worker/Rsh: return maxrc properly for Rsh Worker (#448) + + * xCAT binding: add support for spaces in group names (#459) + + * CLI/Clush: Avoid python3 error with no stdin (#460) + + * CLI/Clush: use os.read() in stdin thread (#463) + + * CLI/Clush: Add maxrc option to clush.conf (#451) + + * CLI/Display: Add support for NO_COLOR and CLICOLOR (#428) (#432) + 2019-12-01 S. Thiell <sthi...@stanford.edu> * Version 1.8.3 released. The main changes are listed below. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/PKG-INFO new/ClusterShell-1.8.4/PKG-INFO --- old/ClusterShell-1.8.3/PKG-INFO 2019-12-17 20:13:15.000000000 +0100 +++ new/ClusterShell-1.8.4/PKG-INFO 2021-11-06 23:20:43.000000000 +0100 @@ -1,12 +1,12 @@ Metadata-Version: 1.1 Name: ClusterShell -Version: 1.8.3 +Version: 1.8.4 Summary: ClusterShell library and tools Home-page: http://clustershell.sourceforge.net/ Author: Stephane Thiell Author-email: sthi...@stanford.edu License: LGPLv2+ -Download-URL: http://sourceforge.net/projects/clustershell/files/clustershell/1.8.3/ +Download-URL: http://sourceforge.net/projects/clustershell/files/clustershell/1.8.4/ Description: ClusterShell is an event-driven open source Python framework, designed to run local or distant commands in parallel on server farms or on large Linux clusters. It will take care of common issues encountered on HPC clusters, such diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/conf/clush.conf new/ClusterShell-1.8.4/conf/clush.conf --- old/ClusterShell-1.8.3/conf/clush.conf 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/conf/clush.conf 2021-11-06 01:59:59.000000000 +0100 @@ -10,6 +10,7 @@ color: auto fd_max: 8192 history_size: 100 +maxrc: no node_count: yes verbosity: 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/conf/groups.conf.d/xcat.conf.example new/ClusterShell-1.8.4/conf/groups.conf.d/xcat.conf.example --- old/ClusterShell-1.8.3/conf/groups.conf.d/xcat.conf.example 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/conf/groups.conf.d/xcat.conf.example 2021-11-06 01:59:59.000000000 +0100 @@ -8,7 +8,7 @@ [xcat] # list the nodes in the specified node group -map: lsdef -s -t node $GROUP | cut -d' ' -f1 +map: lsdef -s -t node "$GROUP" | cut -d' ' -f1 # list all the nodes defined in the xCAT tables all: lsdef -s -t node | cut -d' ' -f1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/conf/groups.d/cluster.yaml.example new/ClusterShell-1.8.4/conf/groups.d/cluster.yaml.example --- old/ClusterShell-1.8.3/conf/groups.d/cluster.yaml.example 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/conf/groups.d/cluster.yaml.example 2021-11-06 01:59:59.000000000 +0100 @@ -39,6 +39,11 @@ # and yes, ranges work for groups too! old: '@rack[1,3]' new: '@rack[2,4]' + # YAML lists + rack5: + - 'example[200-205]' # some comment about example[200-205] + - 'example245' + - 'example300,example[401-406]' # Group source cpu: # define groups @cpu:ivy, @cpu:hsw and @cpu:all diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/extras/vim/syntax/clushconf.vim new/ClusterShell-1.8.4/doc/extras/vim/syntax/clushconf.vim --- old/ClusterShell-1.8.3/doc/extras/vim/syntax/clushconf.vim 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/extras/vim/syntax/clushconf.vim 2021-11-06 01:59:59.000000000 +0100 @@ -16,7 +16,7 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/man/man1/clubak.1 new/ClusterShell-1.8.4/doc/man/man1/clubak.1 --- old/ClusterShell-1.8.3/doc/man/man1/clubak.1 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/man/man1/clubak.1 2021-11-06 01:59:59.000000000 +0100 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH CLUBAK 1 "2019-12-01" "1.8.3" "ClusterShell User Manual" +.TH CLUBAK 1 "2021-11-03" "1.8.4" "ClusterShell User Manual" .SH NAME clubak \- format output from clush/pdsh-like output and more . @@ -87,7 +87,7 @@ message tree trace mode; switch to enable \fBClusterShell.MsgTree\fP trace mode, all keys/nodes being kept for each message element of the tree, thus allowing special output gathering .TP .BI \-\-color\fB= WHENCOLOR -whether to use ANSI colors to surround node or nodeset prefix/header with escape sequences to display them in color on the terminal. \fIWHENCOLOR\fP is \fBnever\fP, \fBalways\fP or \fBauto\fP (which use color if standard output refers to a terminal). Color is set to [34m (blue foreground text) and cannot be modified. +\fBclush\fP can use NO_COLOR, CLICOLOR and CLICOLOR_FORCE environment variables. \fB\-\-color\fP command line option always takes precedence over environment variables. NO_COLOR takes precedence over CLICOLOR_FORCE which takes precedence over CLICOLOR. \fB\-\-color\fP tells whether to use ANSI colors to surround node or nodeset prefix/header with escape sequences to display them in color on the terminal. \fIWHENCOLOR\fP is \fBnever\fP, \fBalways\fP or \fBauto\fP (which use color if standard output refers to a terminal). Color is set to [34m (blue foreground text) and cannot be modified. .TP .B \-\-diff show diff between gathered outputs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/man/man1/clush.1 new/ClusterShell-1.8.4/doc/man/man1/clush.1 --- old/ClusterShell-1.8.3/doc/man/man1/clush.1 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/man/man1/clush.1 2021-11-06 01:59:59.000000000 +0100 @@ -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-11-03" "1.8.4" "ClusterShell User Manual" .SH NAME clush \- execute shell commands on a cluster . @@ -234,11 +234,11 @@ .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 -whether to use ANSI colors to surround node or nodeset prefix/header with escape sequences to display them in color on the terminal. \fIWHENCOLOR\fP is \fBnever\fP, \fBalways\fP or \fBauto\fP (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. +\fBclush\fP can use NO_COLOR, CLICOLOR and CLICOLOR_FORCE environment variables. NO_COLOR takes precedence over CLICOLOR_FORCE which takes precedence over CLICOLOR. When \fB\-\-color\fP option is used these environment variables are not taken into account. \fB\-\-color\fP tells whether to use ANSI colors to surround node or nodeset prefix/header with escape sequences to display them in color on the terminal. \fIWHENCOLOR\fP is \fBnever\fP, \fBalways\fP or \fBauto\fP (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. .TP .B \-\-diff show diff between common outputs (find the best reference output by focusing on largest nodeset and also smaller command return code) @@ -281,7 +281,7 @@ limit time for command to run on the node .TP .BI \-R \ WORKER\fP,\fB \ \-\-worker\fB= WORKER -worker name to use for connection (\fBexec\fP, \fBssh\fP, \fBrsh\fP, \fBpdsh\fP), default is \fBssh\fP +worker name to use for connection (\fBexec\fP, \fBssh\fP, \fBrsh\fP, \fBpdsh\fP, or the name of a Python worker module), default is \fBssh\fP .TP .BI \-\-remote\fB= REMOTE whether to enable remote execution: in tree mode, \(aqyes\(aq forces connections to the leaf nodes for execution, \(aqno\(aq establishes connections up to the leaf parent nodes for execution (default is \(aqyes\(aq) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/man/man1/nodeset.1 new/ClusterShell-1.8.4/doc/man/man1/nodeset.1 --- old/ClusterShell-1.8.3/doc/man/man1/nodeset.1 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/man/man1/nodeset.1 2021-11-06 01:59:59.000000000 +0100 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH NODESET 1 "2019-12-01" "1.8.3" "ClusterShell User Manual" +.TH NODESET 1 "2021-11-03" "1.8.4" "ClusterShell User Manual" .SH NAME nodeset \- compute advanced nodeset operations . diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/man/man5/clush.conf.5 new/ClusterShell-1.8.4/doc/man/man5/clush.conf.5 --- old/ClusterShell-1.8.3/doc/man/man5/clush.conf.5 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/man/man5/clush.conf.5 2021-11-06 01:59:59.000000000 +0100 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH CLUSH.CONF 5 "2019-12-01" "1.8.3" "ClusterShell User Manual" +.TH CLUSH.CONF 5 "2021-11-03" "1.8.4" "ClusterShell User Manual" .SH NAME clush.conf \- Configuration file for clush . @@ -82,12 +82,16 @@ ( connect_timeout + command_timeout ). If set to \fI0\fP, no timeout occurs. .TP .B color -Whether to use ANSI colors to surround node or nodeset prefix/header with -escape sequences to display them in color on the terminal. Valid arguments -are \fBnever\fP, \fBalways\fP or \fBauto\fP (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. +\fBclush\fP can use NO_COLOR, CLICOLOR and CLICOLOR_FORCE +environment variables. NO_COLOR takes precedence over CLICOLOR_FORCE which +takes precedence over CLICOLOR. When the option is set in configuration file +environment variables are taken into account only with \fIauto\fP argument. +\fBcolor\fP tells whether to use ANSI colors to surround node or nodeset +prefix/header with escape sequences to display them in color on the terminal. +Valid arguments are \fBnever\fP, \fBalways\fP or \fBauto\fP (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. .TP .B fd_max Maximum number of open file descriptors permitted per clush process (soft @@ -103,6 +107,9 @@ 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? (yes/no) +.TP .B verbosity Set the verbosity level: \fI0\fP (quiet), \fI1\fP (default), \fI2\fP (verbose) or more (debug). @@ -145,6 +152,7 @@ history_size: 100 color: auto fd_max: 10240 +maxrc: no node_count: yes .fi diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/man/man5/groups.conf.5 new/ClusterShell-1.8.4/doc/man/man5/groups.conf.5 --- old/ClusterShell-1.8.3/doc/man/man5/groups.conf.5 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/man/man5/groups.conf.5 2021-11-06 01:59:59.000000000 +0100 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH GROUPS.CONF 5 "2019-12-01" "1.8.3" "ClusterShell User Manual" +.TH GROUPS.CONF 5 "2021-11-03" "1.8.4" "ClusterShell User Manual" .SH NAME groups.conf \- Configuration file for ClusterShell node groups . diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/sphinx/conf.py new/ClusterShell-1.8.4/doc/sphinx/conf.py --- old/ClusterShell-1.8.3/doc/sphinx/conf.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/sphinx/conf.py 2021-11-06 01:59:59.000000000 +0100 @@ -48,9 +48,9 @@ # built documents. # # The short X.Y version. -version = '1.8.3' +version = '1.8.4' # The full version, including alpha/beta/rc tags. -release = '1.8.3' +release = '1.8.4' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/sphinx/config.rst new/ClusterShell-1.8.4/doc/sphinx/config.rst --- old/ClusterShell-1.8.3/doc/sphinx/config.rst 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/sphinx/config.rst 2021-11-06 01:59:59.000000000 +0100 @@ -64,6 +64,13 @@ | 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). | +-----------------+----------------------------------------------------+ @@ -236,6 +243,11 @@ compute: 'node[0001-0288]' gpu: 'node[0001-0008]' + servers: # example of yaml list syntax for nodes + - 'server001' # in a group + - 'server002,server101' + - 'server[003-006]' + cpu_only: '@compute!@gpu' # example of inline set operation # define group @cpu_only with node[0009-0288] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/sphinx/release.rst new/ClusterShell-1.8.4/doc/sphinx/release.rst --- old/ClusterShell-1.8.3/doc/sphinx/release.rst 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/sphinx/release.rst 2021-11-06 01:59:59.000000000 +0100 @@ -13,6 +13,26 @@ .. warning:: Support for Python 2.5 and below has been dropped in this version. +Version 1.8.4 +^^^^^^^^^^^^^ + +This version contains a few bug fixes and improvements: + +* allow out-of-tree worker modules + +* use default local_worker and allow overriding :ref:`defaults-config` (tree mode) + +* return maxrc properly in the case of the Rsh Worker + +* :ref:`clush-tool`: improve stdin support with Python 3 + +* :ref:`clush-tool`: add maxrc option to :ref:`clush.conf <clush-config>` + +* :ref:`clush-tool`: add support for NO_COLOR and CLICOLOR + +For more details, please have a look at `GitHub Issues for 1.8.4 milestone`_. + + Version 1.8.3 ^^^^^^^^^^^^^ @@ -564,6 +584,7 @@ .. _GitHub Issues for 1.8.1 milestone: https://github.com/cea-hpc/clustershell/issues?utf8=%E2%9C%93&q=is%3Aissue+milestone%3A1.8.1 .. _GitHub Issues for 1.8.2 milestone: https://github.com/cea-hpc/clustershell/issues?utf8=%E2%9C%93&q=is%3Aissue+milestone%3A1.8.2 .. _GitHub Issues for 1.8.3 milestone: https://github.com/cea-hpc/clustershell/issues?utf8=%E2%9C%93&q=is%3Aissue+milestone%3A1.8.3 +.. _GitHub Issues for 1.8.4 milestone: https://github.com/cea-hpc/clustershell/issues?utf8=%E2%9C%93&q=is%3Aissue+milestone%3A1.8.4 .. _LGPL v2.1+: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html .. _CeCILL-C V1: http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html .. _xCAT: https://xcat.org/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/sphinx/tools/clush.rst new/ClusterShell-1.8.4/doc/sphinx/tools/clush.rst --- old/ClusterShell-1.8.3/doc/sphinx/tools/clush.rst 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/sphinx/tools/clush.rst 2021-11-06 01:59:59.000000000 +0100 @@ -589,6 +589,8 @@ --------------- ok +NO_COLOR, CLICOLOR_FORCE??and CLICOLOR environment variables can also +be used to change the way *clush* uses colors to display messages. .. _clush-worker: @@ -633,6 +635,8 @@ installed; doesn't provide write support (eg. you cannot ``cat file | clush --worker pdsh``); it is primarily an 1-to-n worker example. +Worker modules distributed outside of ClusterShell are also supported by +specifying the case-sensitive full Python module name of a worker module. .. [#] LLNL parallel remote shell utility (https://computing.llnl.gov/linux/pdsh.html) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/txt/clubak.txt new/ClusterShell-1.8.4/doc/txt/clubak.txt --- old/ClusterShell-1.8.3/doc/txt/clubak.txt 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/txt/clubak.txt 2021-11-06 01:59:59.000000000 +0100 @@ -7,9 +7,9 @@ -------------------------------------------------- :Author: Stephane Thiell <sthi...@stanford.edu> -:Date: 2019-12-01 +:Date: 2021-11-03 :Copyright: GNU Lesser General Public License version 2.1 or later (LGPLv2.1+) -:Version: 1.8.3 +:Version: 1.8.4 :Manual section: 1 :Manual group: ClusterShell User Manual @@ -57,7 +57,7 @@ node / line content separator string (default: `:`) -F, --fast faster but memory hungry mode (preload all messages per node) -T, --tree message tree trace mode; switch to enable ``ClusterShell.MsgTree`` trace mode, all keys/nodes being kept for each message element of the tree, thus allowing special output gathering ---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 refers to a terminal). Color is set to [34m (blue foreground text) and cannot be modified. +--color=WHENCOLOR ``clush`` can use NO_COLOR, CLICOLOR and CLICOLOR_FORCE environment variables. ``--color`` command line option always takes precedence over environment variables. NO_COLOR takes precedence over CLICOLOR_FORCE which takes precedence over CLICOLOR. ``--color`` tells 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 refers to a terminal). Color is set to [34m (blue foreground text) and cannot be modified. --diff show diff between gathered outputs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/txt/cluset.txt new/ClusterShell-1.8.4/doc/txt/cluset.txt --- old/ClusterShell-1.8.3/doc/txt/cluset.txt 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/txt/cluset.txt 2021-11-06 01:59:59.000000000 +0100 @@ -7,9 +7,9 @@ -------------------------------------------- :Author: Stephane Thiell <sthi...@stanford.edu> -:Date: 2019-12-01 +:Date: 2021-11-03 :Copyright: GNU Lesser General Public License version 2.1 or later (LGPLv2.1+) -:Version: 1.8.3 +:Version: 1.8.4 :Manual section: 1 :Manual group: ClusterShell User Manual diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/txt/clush.conf.txt new/ClusterShell-1.8.4/doc/txt/clush.conf.txt --- old/ClusterShell-1.8.3/doc/txt/clush.conf.txt 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/txt/clush.conf.txt 2021-11-06 01:59:59.000000000 +0100 @@ -7,9 +7,9 @@ ------------------------------ :Author: Stephane Thiell, <sthi...@stanford.edu> -:Date: 2019-12-01 +:Date: 2021-11-03 :Copyright: GNU Lesser General Public License version 2.1 or later (LGPLv2.1+) -:Version: 1.8.3 +:Version: 1.8.4 :Manual section: 5 :Manual group: ClusterShell User Manual @@ -54,12 +54,16 @@ ClusterShell library ensures that any commands complete in less than ( connect_timeout + command_timeout ). If set to *0*, no timeout occurs. color - Whether to use ANSI colors to surround node or nodeset prefix/header with - escape sequences to display them in color on the terminal. Valid arguments - are ``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. + ``clush`` can use NO_COLOR, CLICOLOR and CLICOLOR_FORCE + environment variables. NO_COLOR takes precedence over CLICOLOR_FORCE which + takes precedence over CLICOLOR. When the option is set in configuration file + environment variables are taken into account only with `auto` argument. + ``color`` tells whether to use ANSI colors to surround node or nodeset + prefix/header with escape sequences to display them in color on the terminal. + Valid arguments are ``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. fd_max Maximum number of open file descriptors permitted per clush process (soft resource limit for open files). This limit can never exceed the system @@ -71,6 +75,8 @@ 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 +114,7 @@ | history_size: 100 | color: auto | fd_max: 10240 +| maxrc: no | node_count: yes | diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/txt/clush.txt new/ClusterShell-1.8.4/doc/txt/clush.txt --- old/ClusterShell-1.8.3/doc/txt/clush.txt 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/txt/clush.txt 2021-11-06 01:59:59.000000000 +0100 @@ -7,9 +7,9 @@ ----------------------------------- :Author: Stephane Thiell <sthi...@stanford.edu> -:Date: 2019-12-01 +:Date: 2021-11-03 :Copyright: GNU Lesser General Public License version 2.1 or later (LGPLv2.1+) -:Version: 1.8.3 +:Version: 1.8.4 :Manual section: 1 :Manual group: ClusterShell User Manual @@ -159,8 +159,8 @@ -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 - --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. + -S, --maxrc return the largest of command return codes + --color=WHENCOLOR ``clush`` can use NO_COLOR, CLICOLOR and CLICOLOR_FORCE environment variables. NO_COLOR takes precedence over CLICOLOR_FORCE which takes precedence over CLICOLOR. When ``--color`` option is used these environment variables are not taken into account. ``--color`` tells 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) File copying: @@ -183,7 +183,7 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/txt/groups.conf.txt new/ClusterShell-1.8.4/doc/txt/groups.conf.txt --- old/ClusterShell-1.8.3/doc/txt/groups.conf.txt 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/txt/groups.conf.txt 2021-11-06 01:59:59.000000000 +0100 @@ -7,9 +7,9 @@ ----------------------------------------------- :Author: Stephane Thiell, <sthi...@stanford.edu> -:Date: 2019-12-01 +:Date: 2021-11-03 :Copyright: GNU Lesser General Public License version 2.1 or later (LGPLv2.1+) -:Version: 1.8.3 +:Version: 1.8.4 :Manual section: 5 :Manual group: ClusterShell User Manual diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/doc/txt/nodeset.txt new/ClusterShell-1.8.4/doc/txt/nodeset.txt --- old/ClusterShell-1.8.3/doc/txt/nodeset.txt 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/doc/txt/nodeset.txt 2021-11-06 01:59:59.000000000 +0100 @@ -7,9 +7,9 @@ ----------------------------------- :Author: Stephane Thiell <sthi...@stanford.edu> -:Date: 2019-12-01 +:Date: 2021-11-03 :Copyright: GNU Lesser General Public License version 2.1 or later (LGPLv2.1+) -:Version: 1.8.3 +:Version: 1.8.4 :Manual section: 1 :Manual group: ClusterShell User Manual diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/CLI/Clubak.py new/ClusterShell-1.8.4/lib/ClusterShell/CLI/Clubak.py --- old/ClusterShell-1.8.3/lib/ClusterShell/CLI/Clubak.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/CLI/Clubak.py 2021-11-06 01:59:59.000000000 +0100 @@ -107,7 +107,7 @@ if options.interpret_keys == THREE_CHOICES[-1]: # auto? enable_nodeset_key = None # AUTO else: - enable_nodeset_key = (options.interpret_keys == THREE_CHOICES[1]) + enable_nodeset_key = (options.interpret_keys == THREE_CHOICES[2]) # Create new message tree if options.trace_mode: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/CLI/Clush.py new/ClusterShell-1.8.4/lib/ClusterShell/CLI/Clush.py --- old/ClusterShell-1.8.3/lib/ClusterShell/CLI/Clush.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/CLI/Clush.py 2021-11-06 01:59:59.000000000 +0100 @@ -82,15 +82,16 @@ self.master_worker.write(msg) class OutputHandler(EventHandler): - """Base class for clush output handlers.""" + """Base class for generic output handlers.""" - def __init__(self): + def __init__(self, prog=None): EventHandler.__init__(self) self._runtimer = None + self._prog = prog if prog else os.path.basename(sys.argv[0]) def runtimer_init(self, task, ntotal=0): """Init timer for live command-completed progressmeter.""" - thandler = RunTimer(task, ntotal) + thandler = RunTimer(task, ntotal, prog=self._prog) self._runtimer = task.timer(1.33, thandler, interval=1./3., autoclose=True) @@ -134,8 +135,8 @@ class DirectOutputHandler(OutputHandler): """Direct output event handler class.""" - def __init__(self, display): - OutputHandler.__init__(self) + def __init__(self, display, prog=None): + OutputHandler.__init__(self, prog=prog) self._display = display def ev_read(self, worker, node, sname, msg): @@ -149,14 +150,15 @@ verb = VERB_QUIET if self._display.maxrc: verb = VERB_STD - self._display.vprint_err(verb, "clush: %s: " - "exited with exit code %d" % (node, rc)) + self._display.vprint_err(verb, "%s: %s: exited with exit code %d" % + (self._prog, node, rc)) def ev_close(self, worker, timedout): if timedout: nodeset = NodeSet._fromlist1(worker.iter_keys_timeout()) self._display.vprint_err(VERB_QUIET, - "clush: %s: command timeout" % nodeset) + "%s: %s: command timeout" % + (self._prog, nodeset)) self.update_prompt(worker) class DirectProgressOutputHandler(DirectOutputHandler): @@ -180,8 +182,8 @@ class CopyOutputHandler(DirectProgressOutputHandler): """Copy output event handler.""" - def __init__(self, display, reverse=False): - DirectOutputHandler.__init__(self, display) + def __init__(self, display, reverse=False, prog=None): + DirectOutputHandler.__init__(self, display, prog=prog) self.reverse = reverse def ev_close(self, worker, timedout): @@ -204,10 +206,10 @@ DirectOutputHandler.ev_close(self, worker, timedout) class GatherOutputHandler(OutputHandler): - """Gathered output event handler class (clush -b).""" + """Gathered output event handler class (e.g. clush -b).""" - def __init__(self, display): - OutputHandler.__init__(self) + def __init__(self, display, prog=None): + OutputHandler.__init__(self, prog=prog) self._display = display def ev_read(self, worker, node, sname, msg): @@ -256,16 +258,16 @@ nsdisp = ns = NodeSet._fromlist1(nodelist) if self._display.verbosity > VERB_QUIET and len(ns) > 1: nsdisp = "%s (%d)" % (ns, len(ns)) - msgrc = "clush: %s: exited with exit code %d" % (nsdisp, rc) + msgrc = "%s: %s: exited with exit code %d" % (self._prog, nsdisp, rc) self._display.vprint_err(verbexit, msgrc) # Display nodes that didn't answer within command timeout delay if worker.num_timeout() > 0: - self._display.vprint_err(verbexit, "clush: %s: command timeout" % \ - NodeSet._fromlist1(worker.iter_keys_timeout())) + self._display.vprint_err(verbexit, "%s: %s: command timeout" % \ + (self._prog, NodeSet._fromlist1(worker.iter_keys_timeout()))) class SortedOutputHandler(GatherOutputHandler): - """Sorted by node output event handler class (clush -L).""" + """Sorted by node output event handler class (e.g. clush -L).""" def ev_close(self, worker, timedout): # Overrides GatherOutputHandler.ev_close() @@ -290,9 +292,9 @@ class LiveGatherOutputHandler(GatherOutputHandler): """Live line-gathered output event handler class (-bL).""" - def __init__(self, display, nodes): + def __init__(self, display, nodes, prog=None): assert nodes is not None, "cannot gather local command" - GatherOutputHandler.__init__(self, display) + GatherOutputHandler.__init__(self, display, prog=prog) self._nodes = NodeSet(nodes) self._nodecnt = dict.fromkeys(self._nodes, 0) self._mtreeq = [] @@ -346,7 +348,7 @@ class RunTimer(EventHandler): """Running progress timer event handler""" - def __init__(self, task, total): + def __init__(self, task, total, prog=None): EventHandler.__init__(self) self.task = task self.total = total @@ -357,6 +359,7 @@ # updated by worker handler for progress self.start_time = 0 self.bytes_written = 0 + self._prog = prog if prog else os.path.basename(sys.argv[0]) def ev_timer(self, timer): self.update() @@ -390,9 +393,9 @@ if self.bytes_written > 0 or cnt != self.cnt_last: self.cnt_last = cnt # display completed/total clients - towrite = 'clush: %*d/%*d%s%s\r' % (self.tslen, self.total - cnt, - self.tslen, self.total, gwinfo, - wrbwinfo) + towrite = '%s: %*d/%*d%s%s\r' % (self._prog, self.tslen, + self.total - cnt, self.tslen, + self.total, gwinfo, wrbwinfo) self.wholelen = len(towrite) sys.stderr.write(towrite) self.started = True @@ -403,12 +406,13 @@ return self.erase_line() # display completed/total clients - fmt = 'clush: %*d/%*d' + fmt = '%s: %*d/%*d' if force_cr: fmt += '\n' else: fmt += '\r' - sys.stderr.write(fmt % (self.tslen, self.total, self.tslen, self.total)) + sys.stderr.write(fmt % (self._prog, self.tslen, self.total, self.tslen, + self.total)) def signal_handler(signum, frame): @@ -615,13 +619,14 @@ # 64k seems to be perfect with an openssh backend (they issue 64k # reads) ; could consider making it an option for e.g. gsissh. bufsize = 64 * 1024 - # thread loop: blocking read stdin + send messages to specified - # port object - buf = sys_stdin().read(bufsize) # use buffer in Python 3 - while buf: + # thread loop: read stdin + send messages to specified port object + # use os.read() to work around https://bugs.python.org/issue42717 + while True: + buf = os.read(sys_stdin().fileno(), bufsize) + if not buf: + break # send message to specified port object (with ack) stdin_port.msg(buf) - buf = sys_stdin().read(bufsize) except IOError as ex: display.vprint(VERB_VERB, "stdin: %s" % ex) # send a None message to indicate EOF @@ -631,7 +636,7 @@ """Create a stdin->port->worker binding: connect specified worker to stdin with the help of a reader thread and a ClusterShell Port object.""" - assert not sys.stdin.isatty() + assert sys.stdin is not None and not sys.stdin.isatty() # Create a ClusterShell Port object bound to worker's task. This object # is able to receive messages in a thread-safe manner and then will safely # trigger ev_msg() on a specified event handler. @@ -928,7 +933,7 @@ interactive = not len(args) and \ not (options.copy or options.rcopy) # check for foreground ttys presence (input) - stdin_isafgtty = sys.stdin.isatty() and \ + stdin_isafgtty = sys.stdin is not None and sys.stdin.isatty() and \ os.tcgetpgrp(sys.stdin.fileno()) == os.getpgrp() # check for special condition (empty command and stdin not a tty) if interactive and not stdin_isafgtty: @@ -962,7 +967,8 @@ task.set_default("USER_handle_SIGUSR1", user_interaction) task.excepthook = sys.excepthook - task.set_default("USER_stdin_worker", not (sys.stdin.isatty() or \ + task.set_default("USER_stdin_worker", not (sys.stdin is None or \ + sys.stdin.isatty() or \ options.nostdin or \ user_interaction)) display.vprint(VERB_DEBUG, "Create STDIN worker: %s" % \ @@ -1090,7 +1096,7 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/CLI/Config.py new/ClusterShell-1.8.4/lib/ClusterShell/CLI/Config.py --- old/ClusterShell-1.8.3/lib/ClusterShell/CLI/Config.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/CLI/Config.py 2021-11-06 01:59:59.000000000 +0100 @@ -53,9 +53,10 @@ "connect_timeout": "%f" % DEFAULTS.connect_timeout, "command_timeout": "%f" % DEFAULTS.command_timeout, "history_size": "100", - "color": THREE_CHOICES[-1], # auto + "color": THREE_CHOICES[0], # '' "verbosity": "%d" % VERB_STD, "node_count": "yes", + "maxrc": "no", "fd_max": "8192"} def __init__(self, options, filename=None): @@ -94,6 +95,8 @@ 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 @@ -213,6 +216,11 @@ 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)""" return self.getint("Main", "fd_max") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/CLI/Display.py new/ClusterShell-1.8.4/lib/ClusterShell/CLI/Display.py --- old/ClusterShell-1.8.3/lib/ClusterShell/CLI/Display.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/CLI/Display.py 2021-11-06 01:59:59.000000000 +0100 @@ -25,6 +25,7 @@ import difflib import sys +import os from ClusterShell.NodeSet import NodeSet @@ -33,7 +34,7 @@ VERB_STD = 1 VERB_VERB = 2 VERB_DEBUG = 3 -THREE_CHOICES = ["never", "always", "auto"] +THREE_CHOICES = ["", "never", "always", "auto"] WHENCOLOR_CHOICES = THREE_CHOICES # deprecated; use THREE_CHOICES if sys.getdefaultencoding() == 'ascii': @@ -98,6 +99,18 @@ # display may change when 'max return code' option is set self.maxrc = getattr(options, 'maxrc', False) + # Be compliant with NO_COLOR and CLI_COLORS trying to solve #428 + # See https://no-color.org/ and https://bixense.com/clicolors/ + # NO_COLOR takes precedence over CLI_COLORS. --color option always + # takes precedence over any environment variable. + + if options.whencolor is None: + if (config is None) or (config.color == '' or config.color == 'auto'): + if 'NO_COLOR' not in os.environ: + color = self._has_cli_color() + else: + color = False + if color is None: # Should we use ANSI colors? color = False @@ -137,6 +150,26 @@ if hasattr(options, 'debug') and options.debug: self.verbosity = VERB_DEBUG + def _has_cli_color(self): + """Tests CLICOLOR environment variable to determine wether to + use color or not on output.""" + # When CLICOLOR_FORCE is set to something else than 0 + # colors must be used. + if os.getenv("CLICOLOR_FORCE", "0") != "0": + return True + + cli_color = os.getenv("CLICOLOR") + + if cli_color is None: + return None + elif cli_color != "0": + # CLICOLOR is set and colored output should + # be used if stdout is a tty + return sys.stdout.isatty() + else: + # CLICOLOR is set to not display colors. + return False + def flush(self): """flush display object buffers""" # only used to reset diff display for now diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/CLI/OptionParser.py new/ClusterShell-1.8.4/lib/ClusterShell/CLI/OptionParser.py --- old/ClusterShell-1.8.3/lib/ClusterShell/CLI/OptionParser.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/CLI/OptionParser.py 2021-11-06 01:59:59.000000000 +0100 @@ -172,7 +172,7 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/Defaults.py new/ClusterShell-1.8.4/lib/ClusterShell/Defaults.py --- old/ClusterShell-1.8.3/lib/ClusterShell/Defaults.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/Defaults.py 2021-11-06 01:59:59.000000000 +0100 @@ -55,18 +55,31 @@ """ Return the class pointer matching `workername`. + This can be the 'short' name (such as `ssh`) or a fully-qualified + module path (such as ClusterShell.Worker.Ssh). + The module is loaded if not done yet. """ - modname = "ClusterShell.Worker.%s" % workername.capitalize() + # First try the worker name as a module under ClusterShell.Worker, + # but if that fails, try the worker name directly + try: + modname = "ClusterShell.Worker.%s" % workername.capitalize() + _import_module(modname) + except ImportError: + modname = workername + _import_module(modname) + + # Get the class pointer + return sys.modules[modname].WORKER_CLASS + +def _import_module(modname): + """Import a python module if not done yet.""" # Iterate over a copy of sys.modules' keys to avoid RuntimeError if modname.lower() not in [mod.lower() for mod in list(sys.modules)]: # Import module if not yet loaded __import__(modname) - # Get the class pointer - return sys.modules[modname].WORKER_CLASS - def _local_workerclass(defaults): """Return default local worker class.""" return _load_workerclass(defaults.local_workername) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/Gateway.py new/ClusterShell-1.8.4/lib/ClusterShell/Gateway.py --- old/ClusterShell-1.8.3/lib/ClusterShell/Gateway.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/Gateway.py 2021-11-06 01:59:59.000000000 +0100 @@ -255,6 +255,11 @@ task._info.update(taskinfo) task.set_info('print_debug', _gw_print_debug) + for infokey in taskinfo: + if infokey.startswith('tree_default:'): + self.logger.debug('Setting default %s to %s', infokey[13:], taskinfo[infokey]) + task.set_default(infokey[13:], taskinfo[infokey]) + if task.info('debug'): self.logger.setLevel(logging.DEBUG) @@ -324,6 +329,14 @@ logger = logging.getLogger(__name__) sys.excepthook = gateway_excepthook + if sys.stdin is None: + logger.critical('Gateway failure: sys.stdin is None') + sys.exit(1) + + if sys.stdin.isatty(): + logger.critical('Gateway failure: sys.stdin.isatty() is True') + sys.exit(1) + logger.debug('Starting gateway on %s', host) logger.debug("environ=%s", os.environ) @@ -338,10 +351,6 @@ task.set_default("stdout_msgtree", False) task.set_default("stderr_msgtree", False) - if sys.stdin.isatty(): - logger.critical('Gateway failure: sys.stdin.isatty() is True') - sys.exit(1) - gateway = GatewayChannel(task) worker = StreamWorker(handler=gateway) # Define worker._fanout to not rely on the engine's fanout, and use diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/NodeUtils.py new/ClusterShell-1.8.4/lib/ClusterShell/NodeUtils.py --- old/ClusterShell-1.8.3/lib/ClusterShell/NodeUtils.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/NodeUtils.py 2021-11-06 01:59:59.000000000 +0100 @@ -445,6 +445,8 @@ result = [] assert source raw = getattr(source, 'resolv_%s' % what)(*args) + if isinstance(raw, list): + raw = ','.join(raw) for line in raw.splitlines(): [result.append(x) for x in line.strip().split()] return result diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/RangeSet.py new/ClusterShell-1.8.4/lib/ClusterShell/RangeSet.py --- old/ClusterShell-1.8.3/lib/ClusterShell/RangeSet.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/RangeSet.py 2021-11-06 01:59:59.000000000 +0100 @@ -956,8 +956,7 @@ for rgvec in self._veclist: iveclist += product(*rgvec) assert(len(iveclist) == len(self)) - rnd = RangeSetND(iveclist[index], - pads=[rg.padding for rg in self._veclist[0]], + rnd = RangeSetND(iveclist[index], pads=self.pads(), autostep=self.autostep) return rnd diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/Task.py new/ClusterShell-1.8.4/lib/ClusterShell/Task.py --- old/ClusterShell-1.8.3/lib/ClusterShell/Task.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/Task.py 2021-11-06 01:59:59.000000000 +0100 @@ -58,7 +58,7 @@ basestring = str from ClusterShell.Defaults import config_paths, DEFAULTS -from ClusterShell.Defaults import _local_workerclass, _distant_workerclass +from ClusterShell.Defaults import _local_workerclass, _distant_workerclass, _load_workerclass from ClusterShell.Engine.Engine import EngineAbortException from ClusterShell.Engine.Engine import EngineTimeoutException from ClusterShell.Engine.Engine import EngineAlreadyRunningError @@ -470,6 +470,10 @@ self._default_lock.acquire() try: self._default[default_key] = value + if default_key == 'local_workername': + self._default['local_worker'] = _load_workerclass(value) + elif default_key == 'distant_workername': + self._default['distant_worker'] = _load_workerclass(value) finally: self._default_lock.release() @@ -510,6 +514,8 @@ - "command_timeout": Time in seconds to wait for a command to complete before aborting (default: 0, which means unlimited). + - "tree_default:<key>": In tree mode, overrides the key <key> + in Defaults (settings normally set in defaults.conf) Threading considerations ======================== diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/Worker/Rsh.py new/ClusterShell-1.8.4/lib/ClusterShell/Worker/Rsh.py --- old/ClusterShell-1.8.3/lib/ClusterShell/Worker/Rsh.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/Worker/Rsh.py 2021-11-06 01:59:59.000000000 +0100 @@ -26,6 +26,7 @@ import os import shlex +import re from ClusterShell.Worker.Exec import ExecClient, CopyClient, ExecWorker @@ -35,6 +36,12 @@ Rsh EngineClient. """ + def __init__(self, node, command, worker, stderr, timeout, autoclose=False, + rank=None): + ExecClient.__init__(self, node, command, worker, stderr, timeout, + autoclose, rank) + self.rsh_rc = None + def _build_cmd(self): """ Build the shell command line to start the rsh commmand. @@ -59,8 +66,26 @@ cmd_l.append("%s" % self.key) # key is the node cmd_l.append("%s" % self.command) + # rsh does not properly return exit status + # force the exit status to be printed out + cmd_l.append("; echo XXRETCODE: $?") + return (cmd_l, None) + def _on_nodeset_msgline(self, nodes, msg, sname): + """Override _on_nodeset_msgline to parse magic return code""" + match = re.search(r"^XXRETCODE: (\d+)$", msg.decode()) + if match: + self.rsh_rc = int(match.group(1)) + else: + ExecClient._on_nodeset_msgline(self, nodes, msg, sname) + + def _on_nodeset_close(self, nodes, rc): + """Override _on_nodeset_close to return rsh_rc""" + if (rc == 0 or rc == 1) and self.rsh_rc is not None: + rc = self.rsh_rc + ExecClient._on_nodeset_close(self, nodes, rc) + class RcpClient(CopyClient): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/Worker/Ssh.py new/ClusterShell-1.8.4/lib/ClusterShell/Worker/Ssh.py --- old/ClusterShell-1.8.3/lib/ClusterShell/Worker/Ssh.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/Worker/Ssh.py 2021-11-06 01:59:59.000000000 +0100 @@ -125,18 +125,18 @@ if self.reverse: if user: - cmd_l.append("%s@%s:%s" % (user, self.key, self.source)) + cmd_l.append("%s@[%s]:%s" % (user, self.key, self.source)) else: - cmd_l.append("%s:%s" % (self.key, self.source)) + cmd_l.append("[%s]:%s" % (self.key, self.source)) cmd_l.append(os.path.join(self.dest, "%s.%s" % \ (os.path.basename(self.source), self.key))) else: cmd_l.append(self.source) if user: - cmd_l.append("%s@%s:%s" % (user, self.key, self.dest)) + cmd_l.append("%s@[%s]:%s" % (user, self.key, self.dest)) else: - cmd_l.append("%s:%s" % (self.key, self.dest)) + cmd_l.append("[%s]:%s" % (self.key, self.dest)) return (cmd_l, None) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/Worker/Tree.py new/ClusterShell-1.8.4/lib/ClusterShell/Worker/Tree.py --- old/ClusterShell-1.8.3/lib/ClusterShell/Worker/Tree.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/Worker/Tree.py 2021-11-06 01:59:59.000000000 +0100 @@ -280,11 +280,12 @@ tree=False) else: assert self.source is None - worker = ExecWorker(nodes=targets, - command=self.command, - handler=self.metahandler, - timeout=self.timeout, - stderr=self.stderr) + workerclass = self.task.default('local_worker') + worker = workerclass(nodes=targets, + command=self.command, + handler=self.metahandler, + timeout=self.timeout, + stderr=self.stderr) self.task.schedule(worker) self.workers.append(worker) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell/__init__.py new/ClusterShell-1.8.4/lib/ClusterShell/__init__.py --- old/ClusterShell-1.8.3/lib/ClusterShell/__init__.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell/__init__.py 2021-11-06 01:59:59.000000000 +0100 @@ -34,8 +34,8 @@ - ClusterShell.Task """ -__version__ = '1.8.3' +__version__ = '1.8.4' __version_info__ = tuple([ int(_n) for _n in __version__.split('.')]) -__date__ = '2019/12/01' +__date__ = '2021/11/03' __author__ = 'Stephane Thiell <sthi...@stanford.edu>' __url__ = 'http://clustershell.readthedocs.org/' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/lib/ClusterShell.egg-info/PKG-INFO new/ClusterShell-1.8.4/lib/ClusterShell.egg-info/PKG-INFO --- old/ClusterShell-1.8.3/lib/ClusterShell.egg-info/PKG-INFO 2019-12-17 20:13:09.000000000 +0100 +++ new/ClusterShell-1.8.4/lib/ClusterShell.egg-info/PKG-INFO 2021-11-06 23:20:43.000000000 +0100 @@ -1,12 +1,12 @@ Metadata-Version: 1.1 Name: ClusterShell -Version: 1.8.3 +Version: 1.8.4 Summary: ClusterShell library and tools Home-page: http://clustershell.sourceforge.net/ Author: Stephane Thiell Author-email: sthi...@stanford.edu License: LGPLv2+ -Download-URL: http://sourceforge.net/projects/clustershell/files/clustershell/1.8.3/ +Download-URL: http://sourceforge.net/projects/clustershell/files/clustershell/1.8.4/ Description: ClusterShell is an event-driven open source Python framework, designed to run local or distant commands in parallel on server farms or on large Linux clusters. It will take care of common issues encountered on HPC clusters, such diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/setup.py new/ClusterShell-1.8.4/setup.py --- old/ClusterShell-1.8.3/setup.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/setup.py 2021-11-06 01:59:59.000000000 +0100 @@ -1,7 +1,7 @@ #!/usr/bin/env python # # Copyright (C) 2008-2016 CEA/DAM -# Copyright (C) 2016-2019 Stephane Thiell <sthi...@stanford.edu> +# Copyright (C) 2016-2021 Stephane Thiell <sthi...@stanford.edu> # # This file is part of ClusterShell. # @@ -23,7 +23,7 @@ from setuptools import setup, find_packages -VERSION = '1.8.3' +VERSION = '1.8.4' # Default CFGDIR: in-prefix config install (rpmbuild or pip as user) CFGDIR = 'etc/clustershell' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/tests/CLIClushTest.py new/ClusterShell-1.8.4/tests/CLIClushTest.py --- old/ClusterShell-1.8.3/tests/CLIClushTest.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/tests/CLIClushTest.py 2021-11-06 01:59:59.000000000 +0100 @@ -275,6 +275,14 @@ 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, @@ -606,7 +614,7 @@ class BrokenStdinMock(object): def isatty(self): return False - def read(self, bufsize=1024): + def fileno(self): raise IOError(errno.EINVAL, "Invalid argument") sys.stdin = BrokenStdinMock() @@ -670,3 +678,50 @@ self._clush_t(["--groupsconf", self.custf.name, "-R", "exec", "-w", "@foo", "-bL", "echo ok"], None, b"custom[7-42]: ok\n", 0, b"") + + +class CLIClushTest_D_StdinFIFO(unittest.TestCase): + """Unit test class for testing CLI/Clush.py when stdin is a named pipe""" + + def setUp(self): + # Create fifo + self.fname = tempfile.mktemp() + os.mkfifo(self.fname) + + # Launch write thread + class FIFOThread(threading.Thread): + def run(self): + fifo_wr = open(self.fname, "w") + fifo_wr.write("0123456789") + fifo_wr.close() + + fifoth = FIFOThread() + fifoth.fname = self.fname + fifoth.start() + + # Use read end of fifo as stdin + sys.stdin = open(self.fname, "r") + + def tearDown(self): + """cleanup all tasks""" + sys.stdin.close() + os.unlink(self.fname) + task_cleanup() + sys.stdin = sys.__stdin__ + + def _clush_t(self, args, stdin, expected_stdout, expected_rc=0, + expected_stderr=None): + CLI_main(self, main, ['clush'] + args, stdin, expected_stdout, + expected_rc, expected_stderr) + + def test_300_fifo_stdin(self): + """test clush with stdin is a fifo (read)""" + s = "%s: 0123456789\n" % HOSTNAME + self._clush_t(["-w", HOSTNAME, "-v", "cat"], None, + s.encode(), 0, b"") + + def test_301_fifo_stdin(self): + """test clush with stdin is a fifo (not read)""" + s = "%s: ok\n" % HOSTNAME + self._clush_t(["-w", HOSTNAME, "-v", "echo ok"], None, + s.encode(), 0, b"") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/tests/CLIConfigTest.py new/ClusterShell-1.8.4/tests/CLIConfigTest.py --- old/ClusterShell-1.8.3/tests/CLIConfigTest.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/tests/CLIConfigTest.py 2021-11-06 01:59:59.000000000 +0100 @@ -35,9 +35,10 @@ parser.install_connector_options() options, _ = parser.parse_args([]) config = ClushConfig(options, filename=f.name) - self.assertEqual(config.color, WHENCOLOR_CHOICES[-1]) + self.assertEqual(config.color, THREE_CHOICES[0]) 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) @@ -58,8 +59,9 @@ parser.install_connector_options() options, _ = parser.parse_args([]) config = ClushConfig(options, filename=f.name) - self.assertEqual(config.color, WHENCOLOR_CHOICES[-1]) + self.assertEqual(config.color, THREE_CHOICES[0]) 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) @@ -94,8 +96,9 @@ display = Display(options, config) display.vprint(VERB_STD, "test") display.vprint(VERB_DEBUG, "shouldn't see this") - self.assertEqual(config.color, WHENCOLOR_CHOICES[2]) + self.assertEqual(config.color, THREE_CHOICES[-1]) 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 @@ command_timeout: 0 history_size: 100 color: auto + maxrc: yes node_count: yes verbosity: 1 ssh_user: root @@ -130,8 +134,9 @@ parser.install_connector_options() options, _ = parser.parse_args([]) config = ClushConfig(options, filename=f.name) - self.assertEqual(config.color, WHENCOLOR_CHOICES[2]) + self.assertEqual(config.color, THREE_CHOICES[-1]) 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) @@ -293,7 +298,7 @@ display = Display(options, config) display.vprint(VERB_STD, "test") display.vprint(VERB_DEBUG, "test") - self.assertEqual(config.color, WHENCOLOR_CHOICES[1]) + self.assertEqual(config.color, THREE_CHOICES[2]) self.assertEqual(config.verbosity, VERB_DEBUG) # takes biggest self.assertEqual(config.fanout, 36) self.assertEqual(config.connect_timeout, 7) @@ -355,7 +360,7 @@ parser.install_connector_options() options, _ = parser.parse_args([]) config = ClushConfig(options) # filename=None to use defaults! - self.assertEqual(config.color, WHENCOLOR_CHOICES[0]) + self.assertEqual(config.color, THREE_CHOICES[1]) self.assertEqual(config.verbosity, VERB_VERB) # takes biggest self.assertEqual(config.fanout, 42) self.assertEqual(config.connect_timeout, 14) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/tests/CLIDisplayTest.py new/ClusterShell-1.8.4/tests/CLIDisplayTest.py --- old/ClusterShell-1.8.3/tests/CLIDisplayTest.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/tests/CLIDisplayTest.py 2021-11-06 01:59:59.000000000 +0100 @@ -5,9 +5,10 @@ import tempfile import unittest +import os from io import BytesIO -from ClusterShell.CLI.Display import Display, WHENCOLOR_CHOICES, VERB_STD +from ClusterShell.CLI.Display import Display, THREE_CHOICES, VERB_STD from ClusterShell.CLI.OptionParser import OptionParser from ClusterShell.MsgTree import MsgTree @@ -38,27 +39,46 @@ mtree.add("hostfoo", b"message0") mtree.add("hostfoo", b"message1") - for whencolor in WHENCOLOR_CHOICES: # test whencolor switch - for label in [True, False]: # test no-label switch - options.label = label - options.whencolor = whencolor - disp = Display(options) - # inhibit output - disp.out = BytesIO() - disp.err = BytesIO() - # test print_* methods... - disp.print_line(ns, b"foo bar") - disp.print_line_error(ns, b"foo bar") - disp.print_gather(ns, list(mtree.walk())[0][0]) - # test also string nodeset as parameter - disp.print_gather("hostfoo", list(mtree.walk())[0][0]) - # test line_mode property - self.assertEqual(disp.line_mode, False) - disp.line_mode = True - self.assertEqual(disp.line_mode, True) - disp.print_gather("hostfoo", list(mtree.walk())[0][0]) - disp.line_mode = False - self.assertEqual(disp.line_mode, False) + list_env_vars = [] + list_env_vars.append(dict()) + list_env_vars.append(dict(NO_COLOR='0')) + list_env_vars.append(dict(CLICOLOR='0')) + list_env_vars.append(dict(CLICOLOR='1')) + list_env_vars.append(dict(CLICOLOR='0', CLICOLOR_FORCE='0')) + list_env_vars.append(dict(CLICOLOR_FORCE='1')) + + for env_vars in list_env_vars: + for var_name in env_vars: + var_value = env_vars[var_name] + os.environ[var_name] = var_value + + for whencolor in THREE_CHOICES: # test whencolor switch + if whencolor == "": + options.whencolor = None + else: + options.whencolor = whencolor + for label in [True, False]: # test no-label switch + options.label = label + disp = Display(options) + # inhibit output + disp.out = BytesIO() + disp.err = BytesIO() + # test print_* methods... + disp.print_line(ns, b"foo bar") + disp.print_line_error(ns, b"foo bar") + disp.print_gather(ns, list(mtree.walk())[0][0]) + # test also string nodeset as parameter + disp.print_gather("hostfoo", list(mtree.walk())[0][0]) + # test line_mode property + self.assertEqual(disp.line_mode, False) + disp.line_mode = True + self.assertEqual(disp.line_mode, True) + disp.print_gather("hostfoo", list(mtree.walk())[0][0]) + disp.line_mode = False + self.assertEqual(disp.line_mode, False) + + for var_name in env_vars: + os.environ.pop(var_name) def testDisplayRegroup(self): """test CLI.Display (regroup)""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/tests/DefaultsTest.py new/ClusterShell-1.8.4/tests/DefaultsTest.py --- old/ClusterShell-1.8.3/tests/DefaultsTest.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/tests/DefaultsTest.py 2021-11-06 01:59:59.000000000 +0100 @@ -3,10 +3,14 @@ """Unit test for ClusterShell.Defaults""" +import os +import sys +import shutil + from textwrap import dedent import unittest -from TLib import make_temp_file +from TLib import make_temp_file, make_temp_dir from ClusterShell.Defaults import Defaults, _task_print_debug @@ -98,6 +102,21 @@ self.assertTrue(task.default("distant_worker") is WorkerSsh) task_terminate() + dname = make_temp_dir() + modfile = open(os.path.join(dname, 'OutOfTree.py'), 'w') + modfile.write(dedent(""" + class OutOfTreeWorker(object): + pass + WORKER_CLASS = OutOfTreeWorker""")) + modfile.flush() + modfile.close() + sys.path.append(dname) + self.defaults.distant_workername = 'OutOfTree' + task = task_self(self.defaults) + self.assertTrue(task.default("distant_worker").__name__ is 'OutOfTreeWorker') + task_terminate() + shutil.rmtree(dname, ignore_errors=True) + def test_005_misc_value_errors(self): """test Defaults misc value errors""" task_terminate() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/tests/NodeSetGroupTest.py new/ClusterShell-1.8.4/tests/NodeSetGroupTest.py --- old/ClusterShell-1.8.3/tests/NodeSetGroupTest.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/tests/NodeSetGroupTest.py 2021-11-06 01:59:59.000000000 +0100 @@ -1589,6 +1589,19 @@ self.assertRaises(GroupResolverConfigError, YAMLGroupLoader, f.name) + def test_list_group(self): + f = make_temp_file(dedent(""" + rednecks: + bubba: + - pickup-1 + - pickup-2 + - tractor-[1-2]""").encode('ascii')) + loader = YAMLGroupLoader(f.name) + sources = list(loader) + resolver = GroupResolver(sources[0]) + self.assertEqual(resolver.group_nodes('bubba'), + [ 'pickup-1,pickup-2,tractor-[1-2]' ]) + class GroupResolverYAMLTest(unittest.TestCase): def setUp(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/tests/RangeSetNDTest.py new/ClusterShell-1.8.4/tests/RangeSetNDTest.py --- old/ClusterShell-1.8.3/tests/RangeSetNDTest.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/tests/RangeSetNDTest.py 2021-11-06 01:59:59.000000000 +0100 @@ -432,6 +432,9 @@ # steps self.assertEqual(str(rn1[0:12:2]), "0-3; 1\n10; 10,12\n") self.assertEqual(str(rn1[1:12:2]), "0-3; 2\n10; 11,13\n") + # GitHub #429 + rn1 = RangeSetND([["110", "15-16"], ["107", "06"]]) + self.assertEqual(str(rn1[0:3:2]), "107; 06\n110; 15\n") def test_contiguous(self): rn0 = RangeSetND() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/tests/TLib.py new/ClusterShell-1.8.4/tests/TLib.py --- old/ClusterShell-1.8.3/tests/TLib.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/tests/TLib.py 2021-11-06 01:59:59.000000000 +0100 @@ -39,6 +39,7 @@ def isatty(self): return False + def load_cfg(name): """Load test configuration file as a new ConfigParser""" cfgparser = configparser.ConfigParser() @@ -88,14 +89,16 @@ # Capture standard streams - # Input: if defined, stdin may either be a buffer or a string (with an - # encoding). + # Input: if defined, the stdin argument specifies input data if stdin is not None: - if type(stdin) is bytes: # also works for str in Python 2 - sys.stdin = TBytesIO(stdin) + if type(stdin) is bytes: + # Use temporary file in Python 2 or with buffer (bytes) in Python 3 + sys.stdin = tempfile.TemporaryFile() + sys.stdin.write(stdin) + sys.stdin.seek(0) # ready to be read else: # If stdin is a string in Python 3, use StringIO as sys.stdin - # should be read in text mode for some tests. + # should be read in text mode for some tests (eg. Nodeset). sys.stdin = StringIO(stdin) # Output: ClusterShell sends bytes to sys_stdout()/sys_stderr() and when @@ -110,7 +113,10 @@ finally: sys.stdout = saved_stdout sys.stderr = saved_stderr - sys.stdin = saved_stdin + # close temporary file if we used one for stdin + if saved_stdin != sys.stdin: + sys.stdin.close() + sys.stdin = saved_stdin try: if expected_stdout is not None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ClusterShell-1.8.3/tests/TreeWorkerTest.py new/ClusterShell-1.8.4/tests/TreeWorkerTest.py --- old/ClusterShell-1.8.3/tests/TreeWorkerTest.py 2019-12-07 21:34:33.000000000 +0100 +++ new/ClusterShell-1.8.4/tests/TreeWorkerTest.py 2021-11-06 01:59:59.000000000 +0100 @@ -191,6 +191,22 @@ self.assertEqual(teh.ev_close_cnt, 1) self.assertEqual(teh.last_read, NODE_DISTANT.encode('ascii')) + def test_tree_run_noremote_alt_localworker(self): + """test tree run with remote=False and a non-exec localworker""" + teh = TEventHandler() + self.task.set_info('tree_default:local_workername', 'ssh') + self.task.run('echo %h', nodes=NODE_DISTANT, handler=teh, remote=False) + self.assertEqual(teh.ev_start_cnt, 1) + self.assertEqual(teh.ev_pickup_cnt, 1) + self.assertEqual(teh.ev_read_cnt, 1) + self.assertEqual(teh.ev_written_cnt, 0) + self.assertEqual(teh.ev_hup_cnt, 1) + self.assertEqual(teh.ev_timedout_cnt, 0) + self.assertEqual(teh.ev_close_cnt, 1) + # The exec worker will expand %h to the host, but ssh will just echo '%h' + self.assertEqual(teh.last_read, '%h'.encode('ascii')) + del self.task._info['tree_default:local_workername'] + def test_tree_run_direct(self): """test tree run with direct target, in topology""" teh = TEventHandler()