Skip to content

Commit bb49024

Browse files
committed
Defaults: add NodeSet's default folding axis (#401)
Add 'fold_axis' as a tunable of Defaults. If set, this is used by NodeSet to disengage some nD folding. By default, nD folding is tried on all axes. A global Defaults is instantiated by the library, whose values can be set in a config file. Below is an example of use to improve compatiblity with Slurm: /etc/clustershell/defaults.conf: [nodeset] fold_axis: -1 Once set, by default, the library will only fold node sets using the last axis found. Closes #401. Change-Id: I41637c8e18567ae254dfb2118fe2e4420789f802
1 parent 5e337f5 commit bb49024

File tree

4 files changed

+68
-4
lines changed

4 files changed

+68
-4
lines changed

lib/ClusterShell/Defaults.py

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright (C) 2015-2017 Stephane Thiell <[email protected]>
2+
# Copyright (C) 2015-2019 Stephane Thiell <[email protected]>
33
#
44
# This file is part of ClusterShell.
55
#
@@ -42,6 +42,7 @@
4242
#
4343
CFG_SECTION_TASK_DEFAULT = 'task.default'
4444
CFG_SECTION_TASK_INFO = 'task.info'
45+
CFG_SECTION_NODESET = 'nodeset'
4546

4647
#
4748
# Functions
@@ -84,6 +85,20 @@ def config_paths(config_name):
8485
os.path.expanduser('~/.config')),
8586
'clustershell', config_name)]
8687

88+
def _converter_integer_tuple(value):
89+
"""ConfigParser converter for tuple of integers"""
90+
# NOTE: compatible with ConfigParser 'converters' argument (Python 3.5+)
91+
return tuple(int(x) for x in value.split(',') if x.strip())
92+
93+
def _parser_get_integer_tuple(parser, section, option, **kwargs):
94+
"""
95+
Compatible converter for parsing tuple of integers until we can use
96+
converters from new ConfigParser (Python 3.5+).
97+
"""
98+
return _converter_integer_tuple(
99+
ConfigParser.get(parser, section, option, **kwargs))
100+
101+
87102
#
88103
# Classes
89104
#
@@ -93,7 +108,13 @@ class Defaults(object):
93108
94109
The following attributes may be read at any time and also changed
95110
programmatically, for most of them **before** ClusterShell objects
96-
are initialized (like Task):
111+
(Task or NodeSet) are initialized.
112+
113+
NodeSet defaults:
114+
115+
* fold_axis (tuple of axis integers; default is empty tuple ``()``)
116+
117+
Task defaults:
97118
98119
* stderr (boolean; default is ``False``)
99120
* stdin (boolean; default is ``True``)
@@ -181,12 +202,23 @@ class Defaults(object):
181202
#
182203
_TASK_INFO_PKEYS_BL = ['engine', 'print_debug']
183204

205+
#
206+
# Default values for NodeSet
207+
#
208+
_NODESET = {"fold_axis" : ()}
209+
210+
#
211+
# Datatype converters for NodeSet defaults
212+
#
213+
_NODESET_CONVERTERS = {"fold_axis" : _parser_get_integer_tuple}
214+
184215
def __init__(self, filenames):
185216
"""Initialize Defaults from config filenames"""
186217

187218
self._task_default = self._TASK_DEFAULT.copy()
188219
self._task_info = self._TASK_INFO.copy()
189220
self._task_info_pkeys_bl = list(self._TASK_INFO_PKEYS_BL)
221+
self._nodeset = self._NODESET.copy()
190222

191223
config = ConfigParser()
192224
parsed = config.read(filenames)
@@ -212,22 +244,34 @@ def _parse_config(self, config):
212244
except (NoSectionError, NoOptionError):
213245
pass
214246

247+
# NodeSet
248+
for key, conv in self._NODESET_CONVERTERS.items():
249+
try:
250+
self._nodeset[key] = conv(config, CFG_SECTION_NODESET, key)
251+
except (NoSectionError, NoOptionError):
252+
pass
253+
215254
def __getattr__(self, name):
216255
"""Defaults attribute lookup"""
217256
if name in self._task_default:
218257
return self._task_default[name]
219258
elif name in self._task_info:
220259
return self._task_info[name]
260+
elif name in self._nodeset:
261+
return self._nodeset[name]
221262
raise AttributeError(name)
222263

223264
def __setattr__(self, name, value):
224265
"""Defaults attribute assignment"""
225-
if name in ('_task_default', '_task_info', '_task_info_pkeys_bl'):
266+
if name in ('_task_default', '_task_info', '_task_info_pkeys_bl',
267+
'_nodeset'):
226268
object.__setattr__(self, name, value)
227269
elif name in self._task_default:
228270
self._task_default[name] = value
229271
elif name in self._task_info:
230272
self._task_info[name] = value
273+
elif name in self._nodeset:
274+
self._nodeset[name] = value
231275
else:
232276
raise AttributeError(name)
233277

lib/ClusterShell/NodeSet.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
except NameError:
6161
basestring = str
6262

63-
from ClusterShell.Defaults import config_paths
63+
from ClusterShell.Defaults import config_paths, DEFAULTS
6464
import ClusterShell.NodeUtils as NodeUtils
6565

6666
# Import all RangeSet module public objects
@@ -137,6 +137,8 @@ def __init__(self, pattern=None, rangeset=None, copy_rangeset=True,
137137
self._length = 0
138138
self._patterns = {}
139139
self.fold_axis = fold_axis #: iterable over nD 0-indexed axis
140+
if self.fold_axis is None and DEFAULTS.fold_axis:
141+
self.fold_axis = DEFAULTS.fold_axis # non-empty tuple
140142
if pattern:
141143
self._add(pattern, rangeset, copy_rangeset)
142144
elif rangeset:

tests/DefaultsTest.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ def setUp(self):
2323

2424
def test_000_initial(self):
2525
"""test Defaults initial values"""
26+
# nodeset
27+
self.assertEqual(self.defaults.fold_axis, ())
2628
# task_default
2729
self.assertFalse(self.defaults.stderr)
2830
self.assertTrue(self.defaults.stdout_msgtree)
@@ -43,6 +45,9 @@ def test_000_initial(self):
4345

4446
def test_001_setattr(self):
4547
"""test Defaults setattr"""
48+
# nodeset
49+
self.defaults.fold_axis = (0, 2)
50+
self.assertEqual(self.defaults.fold_axis, (0, 2))
4651
# task_default
4752
self.defaults.stderr = True
4853
self.assertTrue(self.defaults.stderr)
@@ -116,6 +121,8 @@ def setUp(self):
116121
self.defaults = None
117122

118123
def _assert_default_values(self):
124+
# nodeset
125+
self.assertEqual(self.defaults.fold_axis, ())
119126
# task_default
120127
self.assertFalse(self.defaults.stderr)
121128
self.assertTrue(self.defaults.stdout_msgtree)
@@ -143,6 +150,9 @@ def test_000_empty(self):
143150
def test_001_defaults(self):
144151
"""test Defaults config file (defaults)"""
145152
conf_test = make_temp_file(dedent("""
153+
[nodeset]
154+
fold_axis:
155+
146156
[task.default]
147157
stderr: false
148158
stdout_msgtree: true
@@ -165,6 +175,9 @@ def test_001_defaults(self):
165175
def test_002_changed(self):
166176
"""test Defaults config file (changed)"""
167177
conf_test = make_temp_file(dedent("""
178+
[nodeset]
179+
fold_axis: -1
180+
168181
[task.default]
169182
stderr: true
170183
stdout_msgtree: false
@@ -182,6 +195,9 @@ def test_002_changed(self):
182195
connect_timeout: 12.5
183196
command_timeout: 30.5""").encode('ascii'))
184197
self.defaults = Defaults(filenames=[conf_test.name])
198+
# nodeset
199+
self.assertEqual(self.defaults.fold_axis, (-1,))
200+
# task_default
185201
self.assertTrue(self.defaults.stderr)
186202
self.assertFalse(self.defaults.stdout_msgtree)
187203
self.assertFalse(self.defaults.stderr_msgtree)

tests/NodeSetTest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,8 @@ def test_nd_fold_axis_errors(self):
570570
self.assertEqual(str(n1), "a[1,3]b2c0,a[1,3]b2c1,a2b[3-5]c1")
571571
n1.fold_axis = RangeSet("0-1") # ok
572572
self.assertEqual(str(n1), "a[1,3]b2c0,a[1,3]b2c1,a2b[3-5]c1")
573+
n1.fold_axis = (0, 1) # ok
574+
self.assertEqual(str(n1), "a[1,3]b2c0,a[1,3]b2c1,a2b[3-5]c1")
573575

574576
def testSimpleNodeSetUpdates(self):
575577
"""test NodeSet simple nodeset-based update()"""

0 commit comments

Comments
 (0)