Skip to content

Commit b7fd686

Browse files
mattaezellthiell
authored andcommitted
Defaults: Allow out-of-tree worker modules
Attempt to load worker modules distributed with ClusterShell as well as custom worker modules distributed outside of ClusterShell. Signed-off-by: Matt Ezell <ezellma@ornl.gov>
1 parent 003813b commit b7fd686

File tree

4 files changed

+40
-6
lines changed

4 files changed

+40
-6
lines changed

doc/man/man1/clush.1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ limit time to connect to a node
281281
limit time for command to run on the node
282282
.TP
283283
.BI \-R \ WORKER\fP,\fB \ \-\-worker\fB= WORKER
284-
worker name to use for connection (\fBexec\fP, \fBssh\fP, \fBrsh\fP, \fBpdsh\fP), default is \fBssh\fP
284+
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
285285
.TP
286286
.BI \-\-remote\fB= REMOTE
287287
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)

doc/sphinx/tools/clush.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,8 @@ By default, ClusterShell supports the following worker identifiers:
633633
installed; doesn't provide write support (eg. you cannot ``cat file | clush
634634
--worker pdsh``); it is primarily an 1-to-n worker example.
635635

636+
Worker modules distributed outside of ClusterShell are also supported by
637+
specifying the case-sensitive full Python module name of a worker module.
636638

637639
.. [#] LLNL parallel remote shell utility
638640
(https://computing.llnl.gov/linux/pdsh.html)

lib/ClusterShell/Defaults.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,31 @@ def _load_workerclass(workername):
5555
"""
5656
Return the class pointer matching `workername`.
5757
58+
This can be the 'short' name (such as `ssh`) or a fully-qualified
59+
module path (such as ClusterShell.Worker.Ssh).
60+
5861
The module is loaded if not done yet.
5962
"""
60-
modname = "ClusterShell.Worker.%s" % workername.capitalize()
6163

64+
# First try the worker name as a module under ClusterShell.Worker,
65+
# but if that fails, try the worker name directly
66+
try:
67+
modname = "ClusterShell.Worker.%s" % workername.capitalize()
68+
_import_module(modname)
69+
except ImportError:
70+
modname = workername
71+
_import_module(modname)
72+
73+
# Get the class pointer
74+
return sys.modules[modname].WORKER_CLASS
75+
76+
def _import_module(modname):
77+
"""Import a python module if not done yet."""
6278
# Iterate over a copy of sys.modules' keys to avoid RuntimeError
6379
if modname.lower() not in [mod.lower() for mod in list(sys.modules)]:
6480
# Import module if not yet loaded
6581
__import__(modname)
6682

67-
# Get the class pointer
68-
return sys.modules[modname].WORKER_CLASS
69-
7083
def _local_workerclass(defaults):
7184
"""Return default local worker class."""
7285
return _load_workerclass(defaults.local_workername)

tests/DefaultsTest.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33

44
"""Unit test for ClusterShell.Defaults"""
55

6+
import os
7+
import sys
8+
import shutil
9+
610
from textwrap import dedent
711
import unittest
812

9-
from TLib import make_temp_file
13+
from TLib import make_temp_file, make_temp_dir
1014

1115
from ClusterShell.Defaults import Defaults, _task_print_debug
1216

@@ -98,6 +102,21 @@ def test_004_workerclass(self):
98102
self.assertTrue(task.default("distant_worker") is WorkerSsh)
99103
task_terminate()
100104

105+
dname = make_temp_dir()
106+
modfile = open(os.path.join(dname, 'OutOfTree.py'), 'w')
107+
modfile.write(dedent("""
108+
class OutOfTreeWorker(object):
109+
pass
110+
WORKER_CLASS = OutOfTreeWorker"""))
111+
modfile.flush()
112+
modfile.close()
113+
sys.path.append(dname)
114+
self.defaults.distant_workername = 'OutOfTree'
115+
task = task_self(self.defaults)
116+
self.assertTrue(task.default("distant_worker").__name__ is 'OutOfTreeWorker')
117+
task_terminate()
118+
shutil.rmtree(dname, ignore_errors=True)
119+
101120
def test_005_misc_value_errors(self):
102121
"""test Defaults misc value errors"""
103122
task_terminate()

0 commit comments

Comments
 (0)