Skip to content

Commit 1287b9c

Browse files
Jenkinsopenstack-gerrit
authored andcommitted
Merge "Add dispatch to all managers"
2 parents d769b15 + 3e6b57a commit 1287b9c

2 files changed

Lines changed: 136 additions & 19 deletions

File tree

ironic_python_agent/hardware.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,46 @@ def _get_managers():
466466
return _global_managers
467467

468468

469+
def dispatch_to_all_managers(method, *args, **kwargs):
470+
"""Dispatch a method to all hardware managers.
471+
472+
Dispatches the given method in priority order as sorted by
473+
`_get_managers`. If the method doesn't exist or raises
474+
IncompatibleHardwareMethodError, it continues to the next hardware manager.
475+
All managers that have hardware support for this node will be called,
476+
and their responses will be added to a dictionary of the form
477+
{HardwareManagerClassName: response}.
478+
479+
:param method: hardware manager method to dispatch
480+
:param *args: arguments to dispatched method
481+
:param **kwargs: keyword arguments to dispatched method
482+
:raises errors.HardwareManagerMethodNotFound: if all managers raise
483+
IncompatibleHardwareMethodError.
484+
:returns: a dictionary with keys for each hardware manager that returns
485+
a response and the value as a list of results from that hardware
486+
manager.
487+
"""
488+
responses = {}
489+
managers = _get_managers()
490+
for manager in managers:
491+
if getattr(manager, method, None):
492+
try:
493+
response = getattr(manager, method)(*args, **kwargs)
494+
except errors.IncompatibleHardwareMethodError:
495+
LOG.debug('HardwareManager {0} does not support {1}'
496+
.format(manager, method))
497+
continue
498+
responses[manager.__class__.__name__] = response
499+
else:
500+
LOG.debug('HardwareManager {0} does not have method {1}'
501+
.format(manager, method))
502+
503+
if responses == {}:
504+
raise errors.HardwareManagerMethodNotFound(method)
505+
506+
return responses
507+
508+
469509
def dispatch_to_managers(method, *args, **kwargs):
470510
"""Dispatch a method to best suited hardware manager.
471511

ironic_python_agent/tests/multi_hardware.py

Lines changed: 96 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import collections
16+
1517
import mock
1618
from oslotest import base as test_base
1719
from stevedore import extension
@@ -21,30 +23,44 @@
2123

2224

2325
def counted(fn):
24-
def wrapper(*args, **kwargs):
25-
wrapper.called += 1
26-
return fn(*args, **kwargs)
27-
wrapper.called = 0
28-
wrapper.__name__ = fn.__name__
26+
def wrapper(self, *args, **kwargs):
27+
try:
28+
counts = self._call_counts
29+
except AttributeError:
30+
counts = self._call_counts = collections.Counter()
31+
counts[fn.__name__] += 1
32+
return fn(self, *args, **kwargs)
2933
return wrapper
3034

3135

3236
class FakeGenericHardwareManager(hardware.HardwareManager):
3337
@counted
3438
def generic_only(self):
35-
return True
39+
return 'generic_only'
40+
41+
@counted
42+
def generic_none(self):
43+
return None
44+
45+
@counted
46+
def specific_none(self):
47+
return 'generic'
48+
49+
@counted
50+
def return_list(self):
51+
return ['generic']
3652

3753
@counted
3854
def specific_only(self):
3955
raise Exception("Test fail: This method should not be called")
4056

4157
@counted
4258
def mainline_fail(self):
43-
return True
59+
return 'generic_mainline_fail'
4460

4561
@counted
4662
def both_succeed(self):
47-
return True
63+
return 'generic_both'
4864

4965
@counted
5066
def unexpected_fail(self):
@@ -58,15 +74,27 @@ def evaluate_hardware_support(self):
5874
class FakeMainlineHardwareManager(hardware.HardwareManager):
5975
@counted
6076
def specific_only(self):
61-
return True
77+
return 'specific_only'
78+
79+
@counted
80+
def generic_none(self):
81+
return 'specific'
82+
83+
@counted
84+
def specific_none(self):
85+
return None
86+
87+
@counted
88+
def return_list(self):
89+
return ['specific']
6290

6391
@counted
6492
def mainline_fail(self):
6593
raise errors.IncompatibleHardwareMethodError
6694

6795
@counted
6896
def both_succeed(self):
69-
return True
97+
return 'specific_both'
7098

7199
@counted
72100
def unexpected_fail(self):
@@ -83,12 +111,12 @@ def setUp(self):
83111
fake_ep = mock.Mock()
84112
fake_ep.module_name = 'fake'
85113
fake_ep.attrs = ['fake attrs']
86-
ext1 = extension.Extension('fake_generic', fake_ep, None,
114+
self.generic_hwm = extension.Extension('fake_generic', fake_ep, None,
87115
FakeGenericHardwareManager())
88-
ext2 = extension.Extension('fake_mainline', fake_ep, None,
116+
self.mainline_hwm = extension.Extension('fake_mainline', fake_ep, None,
89117
FakeMainlineHardwareManager())
90118
self.fake_ext_mgr = extension.ExtensionManager.make_test_instance([
91-
ext1, ext2
119+
self.generic_hwm, self.mainline_hwm
92120
])
93121

94122
self.extension_mgr_patcher = mock.patch('stevedore.ExtensionManager')
@@ -103,30 +131,32 @@ def tearDown(self):
103131
def test_mainline_method_only(self):
104132
hardware.dispatch_to_managers('specific_only')
105133

106-
self.assertEqual(1, FakeMainlineHardwareManager.specific_only.called)
134+
self.assertEqual(
135+
1, self.mainline_hwm.obj._call_counts['specific_only'])
107136

108137
def test_generic_method_only(self):
109138
hardware.dispatch_to_managers('generic_only')
110139

111-
self.assertEqual(1, FakeGenericHardwareManager.generic_only.called)
140+
self.assertEqual(1, self.generic_hwm.obj._call_counts['generic_only'])
112141

113142
def test_both_succeed(self):
114143
"""In the case where both managers will work; only the most specific
115144
manager should have it's function called.
116145
"""
117146
hardware.dispatch_to_managers('both_succeed')
118147

119-
self.assertEqual(1, FakeMainlineHardwareManager.both_succeed.called)
120-
self.assertEqual(0, FakeGenericHardwareManager.both_succeed.called)
148+
self.assertEqual(1, self.mainline_hwm.obj._call_counts['both_succeed'])
149+
self.assertEqual(0, self.generic_hwm.obj._call_counts['both_succeed'])
121150

122151
def test_mainline_fails(self):
123152
"""Ensure that if the mainline manager is unable to run the method
124153
that we properly fall back to generic.
125154
"""
126155
hardware.dispatch_to_managers('mainline_fail')
127156

128-
self.assertEqual(1, FakeMainlineHardwareManager.mainline_fail.called)
129-
self.assertEqual(1, FakeGenericHardwareManager.mainline_fail.called)
157+
self.assertEqual(
158+
1, self.mainline_hwm.obj._call_counts['mainline_fail'])
159+
self.assertEqual(1, self.generic_hwm.obj._call_counts['mainline_fail'])
130160

131161
def test_manager_method_not_found(self):
132162
self.assertRaises(errors.HardwareManagerMethodNotFound,
@@ -138,6 +168,53 @@ def test_method_fails(self):
138168
hardware.dispatch_to_managers,
139169
'unexpected_fail')
140170

171+
def test_dispatch_to_all_managers_mainline_only(self):
172+
results = hardware.dispatch_to_all_managers('generic_none')
173+
174+
self.assertEqual(1, self.generic_hwm.obj._call_counts['generic_none'])
175+
self.assertEqual({'FakeGenericHardwareManager': None,
176+
'FakeMainlineHardwareManager': 'specific'},
177+
results)
178+
179+
def test_dispatch_to_all_managers_generic_method_only(self):
180+
results = hardware.dispatch_to_all_managers('specific_none')
181+
182+
self.assertEqual(1, self.generic_hwm.obj._call_counts['specific_none'])
183+
self.assertEqual({'FakeGenericHardwareManager': 'generic',
184+
'FakeMainlineHardwareManager': None}, results)
185+
186+
def test_dispatch_to_all_managers_both_succeed(self):
187+
"""In the case where both managers will work; only the most specific
188+
manager should have it's function called.
189+
"""
190+
results = hardware.dispatch_to_all_managers('both_succeed')
191+
192+
self.assertEqual({'FakeGenericHardwareManager': 'generic_both',
193+
'FakeMainlineHardwareManager': 'specific_both'},
194+
results)
195+
self.assertEqual(1, self.mainline_hwm.obj._call_counts['both_succeed'])
196+
self.assertEqual(1, self.generic_hwm.obj._call_counts['both_succeed'])
197+
198+
def test_dispatch_to_all_managers_mainline_fails(self):
199+
"""Ensure that if the mainline manager is unable to run the method
200+
that we properly fall back to generic.
201+
"""
202+
hardware.dispatch_to_all_managers('mainline_fail')
203+
204+
self.assertEqual(
205+
1, self.mainline_hwm.obj._call_counts['mainline_fail'])
206+
self.assertEqual(1, self.generic_hwm.obj._call_counts['mainline_fail'])
207+
208+
def test_dispatch_to_all_managers_manager_method_not_found(self):
209+
self.assertRaises(errors.HardwareManagerMethodNotFound,
210+
hardware.dispatch_to_all_managers,
211+
'unknown_method')
212+
213+
def test_dispatch_to_all_managers_method_fails(self):
214+
self.assertRaises(RuntimeError,
215+
hardware.dispatch_to_all_managers,
216+
'unexpected_fail')
217+
141218

142219
class TestNoHardwareManagerLoading(test_base.BaseTestCase):
143220
def setUp(self):

0 commit comments

Comments
 (0)