Merge "Support 'shelve' and 'reboot' instance notifications"

This commit is contained in:
Jenkins 2016-08-02 23:37:46 +00:00 committed by Gerrit Code Review
commit ae09e6ae39
2 changed files with 368 additions and 1 deletions

View File

@ -43,6 +43,25 @@ class InstanceHandler(base.NotificationBase):
_provisioning_states = {'networking': None,
'block_device_mapping': 'networking',
'spawning': 'block_device_mapping'}
_reboot_states = {
# Hard reboot
'reboot_pending_hard': 'rebooting_hard',
'reboot_started_hard': 'reboot_pending_hard',
# Soft reboot
'reboot_pending': 'rebooting',
'reboot_started': 'reboot_pending',
}
_shelve_states = {
'shelving_image_pending_upload': 'shelving',
'shelving_image_uploading': 'shelving_image_pending_upload',
# shelving_image_uploading -> shelving_image_pending_upload and
# shelving_image_pending_upload -> shelving_image_uploading will
# be ignored.
'shelving_offloading': 'shelving_image_uploading',
}
_unshelve_states = {
'spawning': 'unshelving'
}
@classmethod
def _get_notification_exchanges(cls):
@ -68,8 +87,13 @@ class InstanceHandler(base.NotificationBase):
'compute.instance.unpause.end': self.index_from_api,
'compute.instance.shutdown.end': self.index_from_api,
'compute.instance.reboot.end': self.index_from_api,
'compute.instance.delete.end': self.delete,
'compute.instance.shelve.end': self.index_from_api,
'compute.instance.shelve_offload.end': self.index_from_api,
'compute.instance.unshelve.end': self.index_from_api,
'compute.instance.volume.attach': self.index_from_api,
'compute.instance.volume.detach': self.index_from_api,
@ -127,13 +151,43 @@ class InstanceHandler(base.NotificationBase):
# and indicates the end of the init sequence; ignore this one in
# favor of create.end
ignore_update = True
elif (new_state == 'active' and
new_task_state is None and old_task_state is None):
ignore_update = True
elif new_task_state is None and old_task_state is not None:
# These happen right before a corresponding .end event
ignore_update = True
elif new_task_state is not None and new_task_state == old_task_state:
# Ignore spawning -> spawning for shelved_offloaded instance and
# shelving_offloading -> shelving_offloading for shelved instance.
if (new_task_state == 'shelving_offloading' or
(new_state == 'shelved_offloaded' and
new_task_state == 'spawning')):
ignore_update = True
else:
update_if_state_matches = dict(
state=new_state,
new_task_state=None)
elif new_task_state in self._reboot_states:
update_if_state_matches = dict(
state=new_state,
new_task_state=None)
new_task_state=self._reboot_states[new_task_state])
elif new_task_state in self._shelve_states:
if (new_state == 'active' or
(new_state == 'shelved' and
new_task_state == 'shelving_offloading')):
update_if_state_matches = dict(
state='active',
new_task_state=self._shelve_states[new_task_state])
elif new_state == 'shelved_offloaded':
if new_task_state is not None:
update_if_state_matches = dict(
state=new_state,
new_task_state=self._unshelve_states[new_task_state])
else:
update_if_state_matches = dict(
state='shelved',
new_task_state='shelving_offloading')
if ignore_update:
LOG.debug("Skipping update or indexing for %s; event contains no "

View File

@ -977,3 +977,316 @@ class TestServerLoaderPlugin(test_utils.BaseTestCase):
"compute.instance.pause.end",
expect_handled=False)
assert_call_counts(get=1, save=1, es_gets=1)
@mock.patch(nova_version_getter, return_value=fake_version_list)
def test_reboot_state_change_notifications(self, mock_version):
mock_engine = mock.Mock()
self.plugin.engine = mock_engine
reboot_events = [
('compute.instance.update',
dict(state_description='rebooting', state='active',
old_task_state='rebooting', new_task_state='rebooting'),
'2016-07-17 19:52:13.523135'),
('compute.instance.reboot.start',
dict(state_description='rebooting', state='active'),
'2016-07-17 19:52:13.628180'),
('compute.instance.update',
dict(state_description='', state='active',
old_task_state='rebooting', new_task_state='reboot_pending'),
'2016-07-17 19:52:13.771604'),
('compute.instance.update',
dict(state_description='', state='active',
old_task_state='reboot_pending',
new_task_state='reboot_started'),
'2016-07-17 19:52:13.781605'),
('compute.instance.update',
dict(state_description='', state='active',
old_task_state='reboot_started', new_task_state=None),
'2016-07-17 19:52:13.791605'),
('compute.instance.reboot.end',
dict(state_description='', state='active'),
'2016-07-17 19:52:13.808362')
]
for event in reboot_events:
event[1]["instance_id"] = u"a380287d-1f61-4887-959c-8c5ab8f75f8f"
handler = self.plugin.get_notification_handler()
event_handlers = handler.get_event_handlers()
def handle_message(message, expected_event_type, expect_handled=True):
self.assertEqual(expected_event_type, message[0],
"Expected event type doesn't match test "
"message type.")
# type, payload, timestamp
type_handler = event_handlers.get(message[0], None)
if type_handler:
type_handler(message[1], message[2])
else:
if expect_handled:
self.fail("Expected event '%s' to be handled" %
expected_event_type)
with mock.patch.object(self.plugin.index_helper,
'save_documents') as mock_save, \
mock.patch.object(self.plugin.index_helper,
'get_document') as mock_es_getter, \
mock.patch(nova_server_getter,
return_value=self.instance1) as nova_getter:
def assert_call_counts(get=0, save=0, es_gets=0):
# Helper to reduce copy pasta
self.assertEqual(get, nova_getter.call_count)
self.assertEqual(save, mock_save.call_count)
self.assertEqual(es_gets, mock_es_getter.call_count)
handle_message(reboot_events[0],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=1)
handle_message(reboot_events[1],
"compute.instance.reboot.start",
expect_handled=False)
assert_call_counts(get=0, save=0, es_gets=1)
handle_message(reboot_events[2],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=2)
handle_message(reboot_events[3],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=3)
handle_message(reboot_events[4],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=3)
handle_message(reboot_events[5],
"compute.instance.reboot.end",
expect_handled=False)
assert_call_counts(get=1, save=1, es_gets=3)
@mock.patch(nova_version_getter, return_value=fake_version_list)
def test_shelve_state_change_notifications(self, mock_version):
mock_engine = mock.Mock()
self.plugin.engine = mock_engine
shelve_events = [
('compute.instance.update',
dict(state_description='shelving', state='active',
old_task_state='shelving', new_task_state='shelving'),
'2016-07-17 19:52:13.523135'),
('compute.instance.shelve.start',
dict(state_description='shelving', state='active'),
'2016-07-17 19:52:13.628180'),
('compute.instance.update',
dict(state_description='', state='active',
old_task_state='shelving',
new_task_state='shelving_image_pending_upload'),
'2016-07-17 19:52:13.771604'),
('compute.instance.update',
dict(state_description='', state='active',
old_task_state='shelving_image_pending_upload',
new_task_state='shelving_image_uploading'),
'2016-07-17 19:52:13.781605'),
('compute.instance.update',
dict(state_description='', state='active',
old_task_state='shelving_image_uploading',
new_task_state='shelving_image_pending_upload'),
'2016-07-17 19:52:13.791605'),
('compute.instance.update',
dict(state_description='', state='active',
old_task_state='shelving_image_pending_upload',
new_task_state='shelving_image_uploading'),
'2016-07-17 19:52:13.801605'),
('compute.instance.update',
dict(state_description='', state='shelved',
old_task_state='shelving_image_uploading',
new_task_state='shelving_offloading'),
'2016-07-17 19:52:13.811605'),
('compute.instance.update',
dict(state_description='', state='shelved',
old_task_state='shelving_offloading',
new_task_state='shelving_offloading'),
'2016-07-17 19:52:13.821605'),
('compute.instance.update',
dict(state_description='', state='shelved_offloaded',
old_task_state='shelving_offloading',
new_task_state=None),
'2016-07-17 19:52:13.831605'),
('compute.instance.shelve.end',
dict(state_description='', state='active'),
'2016-07-17 19:52:13.858362')
]
for event in shelve_events:
event[1]["instance_id"] = u"a380287d-1f61-4887-959c-8c5ab8f75f8f"
handler = self.plugin.get_notification_handler()
event_handlers = handler.get_event_handlers()
def handle_message(message, expected_event_type, expect_handled=True):
self.assertEqual(expected_event_type, message[0],
"Expected event type doesn't match test "
"message type.")
# type, payload, timestamp
type_handler = event_handlers.get(message[0], None)
if type_handler:
type_handler(message[1], message[2])
else:
if expect_handled:
self.fail("Expected event '%s' to be handled" %
expected_event_type)
with mock.patch.object(self.plugin.index_helper,
'save_documents') as mock_save, \
mock.patch.object(self.plugin.index_helper,
'get_document') as mock_es_getter, \
mock.patch(nova_server_getter,
return_value=self.instance1) as nova_getter:
def assert_call_counts(get=0, save=0, es_gets=0):
# Helper to reduce copy pasta
self.assertEqual(get, nova_getter.call_count)
self.assertEqual(save, mock_save.call_count)
self.assertEqual(es_gets, mock_es_getter.call_count)
handle_message(shelve_events[0],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=1)
handle_message(shelve_events[1],
"compute.instance.shelve.start",
expect_handled=False)
assert_call_counts(get=0, save=0, es_gets=1)
handle_message(shelve_events[2],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=2)
handle_message(shelve_events[3],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=3)
handle_message(shelve_events[4],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=4)
handle_message(shelve_events[5],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=5)
handle_message(shelve_events[6],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=6)
handle_message(shelve_events[7],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=6)
handle_message(shelve_events[8],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=6)
handle_message(shelve_events[9],
"compute.instance.shelve.end",
expect_handled=False)
assert_call_counts(get=1, save=1, es_gets=6)
@mock.patch(nova_version_getter, return_value=fake_version_list)
def test_unshelve_state_change_notifications(self, mock_version):
mock_engine = mock.Mock()
self.plugin.engine = mock_engine
unshelve_events = [
('compute.instance.update',
dict(state_description='unshelving', state='shelved_offloaded',
old_task_state='unshelving', new_task_state='unshelving'),
'2016-07-17 19:52:13.523135'),
('compute.instance.unshelve.start',
dict(state_description='unshelving', state='shelved_offloaded'),
'2016-07-17 19:52:13.628180'),
('compute.instance.update',
dict(state_description='', state='shelved_offloaded',
old_task_state='unshelving', new_task_state='unshelving'),
'2016-07-17 19:52:13.771604'),
('compute.instance.update',
dict(state_description='', state='shelved_offloaded',
old_task_state='unshelving',
new_task_state='spawning'),
'2016-07-17 19:52:13.781605'),
('compute.instance.update',
dict(state_description='', state='shelved_offloaded',
old_task_state='spawning', new_task_state='spawning'),
'2016-07-17 19:52:13.791605'),
('compute.instance.update',
dict(state_description='', state='active',
old_task_state='spawning', new_task_state=None),
'2016-07-17 19:52:13.801605'),
('compute.instance.unshelve.end',
dict(state_description='', state='active'),
'2016-07-17 19:52:13.818362')
]
for event in unshelve_events:
event[1]["instance_id"] = u"a380287d-1f61-4887-959c-8c5ab8f75f8f"
handler = self.plugin.get_notification_handler()
event_handlers = handler.get_event_handlers()
def handle_message(message, expected_event_type, expect_handled=True):
self.assertEqual(expected_event_type, message[0],
"Expected event type doesn't match test "
"message type.")
# type, payload, timestamp
type_handler = event_handlers.get(message[0], None)
if type_handler:
type_handler(message[1], message[2])
else:
if expect_handled:
self.fail("Expected event '%s' to be handled" %
expected_event_type)
with mock.patch.object(self.plugin.index_helper,
'save_documents') as mock_save, \
mock.patch.object(self.plugin.index_helper,
'get_document') as mock_es_getter, \
mock.patch(nova_server_getter,
return_value=self.instance1) as nova_getter:
def assert_call_counts(get=0, save=0, es_gets=0):
# Helper to reduce copy pasta
self.assertEqual(get, nova_getter.call_count)
self.assertEqual(save, mock_save.call_count)
self.assertEqual(es_gets, mock_es_getter.call_count)
handle_message(unshelve_events[0],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=1)
handle_message(unshelve_events[1],
"compute.instance.unshelve.start",
expect_handled=False)
assert_call_counts(get=0, save=0, es_gets=1)
handle_message(unshelve_events[2],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=2)
handle_message(unshelve_events[3],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=3)
handle_message(unshelve_events[4],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=3)
handle_message(unshelve_events[5],
"compute.instance.update")
assert_call_counts(get=0, save=0, es_gets=3)
handle_message(unshelve_events[6],
"compute.instance.unshelve.end",
expect_handled=False)
assert_call_counts(get=1, save=1, es_gets=3)