Merge "Add a way to exit early from a wait_for_instance_event()"
This commit is contained in:
commit
ba4b5f3a6c
|
@ -418,6 +418,22 @@ class ComputeVirtAPI(virtapi.VirtAPI):
|
|||
self._compute = compute
|
||||
self.reportclient = compute.reportclient
|
||||
|
||||
class ExitEarly(Exception):
|
||||
def __init__(self, events):
|
||||
super(Exception, self).__init__()
|
||||
self.events = events
|
||||
|
||||
self._exit_early_exc = ExitEarly
|
||||
|
||||
def exit_wait_early(self, events):
|
||||
"""Exit a wait_for_instance_event() immediately and avoid
|
||||
waiting for some events.
|
||||
|
||||
:param: events: A list of (name, tag) tuples for events that we should
|
||||
skip waiting for during a wait_for_instance_event().
|
||||
"""
|
||||
raise self._exit_early_exc(events=events)
|
||||
|
||||
def _default_error_callback(self, event_name, instance):
|
||||
raise exception.NovaException(_('Instance event failed'))
|
||||
|
||||
|
@ -445,6 +461,17 @@ class ComputeVirtAPI(virtapi.VirtAPI):
|
|||
waiting for the rest of the events, False to stop processing,
|
||||
or raise an exception which will bubble up to the waiter.
|
||||
|
||||
If the inner code wishes to abort waiting for one or more
|
||||
events because it knows some state to be finished or condition
|
||||
to be satisfied, it can use VirtAPI.exit_wait_early() with a
|
||||
list of event (name,tag) items to avoid waiting for those
|
||||
events upon context exit. Note that exit_wait_early() exits
|
||||
the context immediately and should be used to signal that all
|
||||
work has been completed and provide the unified list of events
|
||||
that need not be waited for. Waiting for the remaining events
|
||||
will begin immediately upon early exit as if the context was
|
||||
exited normally.
|
||||
|
||||
:param instance: The instance for which an event is expected
|
||||
:param event_names: A list of event names. Each element is a
|
||||
tuple of strings to indicate (name, tag),
|
||||
|
@ -471,12 +498,25 @@ class ComputeVirtAPI(virtapi.VirtAPI):
|
|||
# should all be canceled and fired immediately below,
|
||||
# but don't stick around if not.
|
||||
deadline = 0
|
||||
yield
|
||||
try:
|
||||
yield
|
||||
except self._exit_early_exc as e:
|
||||
early_events = set([objects.InstanceExternalEvent.make_key(n, t)
|
||||
for n, t in e.events])
|
||||
else:
|
||||
early_events = []
|
||||
|
||||
with eventlet.timeout.Timeout(deadline):
|
||||
for event_name, event in events.items():
|
||||
actual_event = event.wait()
|
||||
if actual_event.status == 'completed':
|
||||
if event_name in early_events:
|
||||
continue
|
||||
else:
|
||||
actual_event = event.wait()
|
||||
if actual_event.status == 'completed':
|
||||
continue
|
||||
# If we get here, we have an event that was not completed,
|
||||
# nor skipped via exit_wait_early(). Decide whether to
|
||||
# keep waiting by calling the error_callback() hook.
|
||||
decision = error_callback(event_name, instance)
|
||||
if decision is False:
|
||||
break
|
||||
|
|
|
@ -48,6 +48,9 @@ class VirtAPIBaseTest(test.NoDBTestCase, test.APICoverage):
|
|||
self.assertExpected('wait_for_instance_event',
|
||||
'instance', ['event'])
|
||||
|
||||
def test_exit_wait_early(self):
|
||||
self.assertExpected('exit_wait_early', [])
|
||||
|
||||
def test_update_compute_provider_status(self):
|
||||
self.assertExpected('update_compute_provider_status',
|
||||
nova_context.get_admin_context(), uuids.rp_uuid,
|
||||
|
@ -67,6 +70,8 @@ class FakeVirtAPITest(VirtAPIBaseTest):
|
|||
with self.virtapi.wait_for_instance_event(*args, **kwargs):
|
||||
run = True
|
||||
self.assertTrue(run)
|
||||
elif method == 'exit_wait_early':
|
||||
self.virtapi.exit_wait_early(*args, **kwargs)
|
||||
elif method == 'update_compute_provider_status':
|
||||
self.virtapi.update_compute_provider_status(*args, **kwargs)
|
||||
else:
|
||||
|
@ -120,6 +125,12 @@ class ComputeVirtAPITest(VirtAPIBaseTest):
|
|||
self.compute = FakeCompute()
|
||||
self.virtapi = compute_manager.ComputeVirtAPI(self.compute)
|
||||
|
||||
def test_exit_wait_early(self):
|
||||
self.assertRaises(self.virtapi._exit_early_exc,
|
||||
self.virtapi.exit_wait_early,
|
||||
[('foo', 'bar'),
|
||||
('foo', 'baz')])
|
||||
|
||||
def test_wait_for_instance_event(self):
|
||||
and_i_ran = ''
|
||||
event_1_tag = objects.InstanceExternalEvent.make_key(
|
||||
|
@ -182,6 +193,22 @@ class ComputeVirtAPITest(VirtAPIBaseTest):
|
|||
|
||||
self.assertRaises(test.TestingException, do_test)
|
||||
|
||||
def test_wait_for_instance_event_exit_early(self):
|
||||
# Wait for two events, exit early skipping one.
|
||||
# Make sure we waited for one and did not wait for the other
|
||||
with self.virtapi.wait_for_instance_event('instance',
|
||||
[('foo', 'bar'),
|
||||
('foo', 'baz')]):
|
||||
self.virtapi.exit_wait_early([('foo', 'baz')])
|
||||
self.fail('never gonna happen')
|
||||
|
||||
self.assertEqual(2, len(self.compute._events))
|
||||
for event in self.compute._events:
|
||||
if event.tag == 'bar':
|
||||
event.wait.assert_called_once_with()
|
||||
else:
|
||||
event.wait.assert_not_called()
|
||||
|
||||
def test_update_compute_provider_status(self):
|
||||
"""Tests scenarios for adding/removing the COMPUTE_STATUS_DISABLED
|
||||
trait on a given compute node resource provider.
|
||||
|
|
|
@ -677,6 +677,10 @@ class FakeVirtAPI(virtapi.VirtAPI):
|
|||
# fall through
|
||||
yield
|
||||
|
||||
def exit_wait_early(self, events):
|
||||
# We never wait, so there is nothing to exit early
|
||||
pass
|
||||
|
||||
def update_compute_provider_status(self, context, rp_uuid, enabled):
|
||||
pass
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@ class VirtAPI(object):
|
|||
error_callback=None):
|
||||
raise NotImplementedError()
|
||||
|
||||
def exit_wait_early(self, events):
|
||||
raise NotImplementedError()
|
||||
|
||||
def update_compute_provider_status(self, context, rp_uuid, enabled):
|
||||
"""Used to add/remove the COMPUTE_STATUS_DISABLED trait on the provider
|
||||
|
||||
|
|
Loading…
Reference in New Issue