Cells: Call compute api methods with instance objects

When nova-cells processes a 'run_compute_api_method' message it pulls
the instance from the local db and calls the appropriate compute api
method with a sqlalchemy model.  Instead it should populate an instance
object and pass that in.

Change-Id: Ib4fb46310c2a2c1edf378ed3a3fd42421ff4cb47
Closes-bug: #1290975
This commit is contained in:
Andrew Laski 2014-03-11 15:28:57 -04:00 committed by Dan Smith
parent fbbc00db47
commit ca908166db
2 changed files with 42 additions and 110 deletions

View File

@ -668,9 +668,15 @@ class _TargetedMessageMethods(_BaseMessageMethods):
# 1st arg is instance_uuid that we need to turn into the
# instance object.
instance_uuid = args[0]
# NOTE: compute/api.py loads these when retrieving an instance for an
# API request, so there's a good chance that this is what was loaded.
expected_attrs = ['metadata', 'system_metadata', 'security_groups',
'info_cache']
try:
instance = self.db.instance_get_by_uuid(message.ctxt,
instance_uuid)
instance = objects.Instance.get_by_uuid(message.ctxt,
instance_uuid, expected_attrs=expected_attrs)
args[0] = instance
except exception.InstanceNotFound:
with excutils.save_and_reraise_exception():
# Must be a race condition. Let's try to resolve it by
@ -679,22 +685,6 @@ class _TargetedMessageMethods(_BaseMessageMethods):
instance = {'uuid': instance_uuid}
self.msg_runner.instance_destroy_at_top(message.ctxt,
instance)
# FIXME(comstud): This is temporary/transitional until I can
# work out a better way to pass full objects down.
EXPECTS_OBJECTS = ['start', 'stop', 'delete_instance_metadata',
'update_instance_metadata', 'shelve', 'unshelve']
if method in EXPECTS_OBJECTS:
inst_obj = objects.Instance()
expected_attrs = None
# shelve and unshelve requires 'info_cache' and 'metadata',
# because of this fetching same from database.
if method in ['shelve', 'unshelve']:
expected_attrs = ['metadata', 'info_cache']
inst_obj._from_db_object(message.ctxt, inst_obj, instance,
expected_attrs=expected_attrs)
instance = inst_obj
args[0] = instance
return fn(message.ctxt, *args, **method_info['method_kwargs'])
def update_capabilities(self, message, cell_name, capabilities):

View File

@ -685,120 +685,62 @@ class CellsTargetedMethodsTestCase(test.TestCase):
self.src_msg_runner.build_instances(self.ctxt, self.tgt_cell_name,
build_inst_kwargs)
def test_run_compute_api_method(self):
instance_uuid = 'fake_instance_uuid'
method_info = {'method': 'backup',
'method_args': (instance_uuid, 2, 3),
'method_kwargs': {'arg1': 'val1', 'arg2': 'val2'}}
self.mox.StubOutWithMock(self.tgt_compute_api, 'backup')
self.mox.StubOutWithMock(self.tgt_db_inst, 'instance_get_by_uuid')
self.tgt_db_inst.instance_get_by_uuid(self.ctxt,
instance_uuid).AndReturn('fake_instance')
self.tgt_compute_api.backup(self.ctxt, 'fake_instance', 2, 3,
arg1='val1', arg2='val2').AndReturn('fake_result')
self.mox.ReplayAll()
response = self.src_msg_runner.run_compute_api_method(
self.ctxt,
self.tgt_cell_name,
method_info,
True)
result = response.value_or_raise()
self.assertEqual('fake_result', result)
def _run_compute_api_method_expects_object(self, tgt_compute_api_function,
method_name,
expected_attrs=None):
# runs compute api methods which expects instance to be an object
instance_uuid = 'fake_instance_uuid'
def _run_compute_api_method(self, method_name):
instance = fake_instance.fake_instance_obj(self.ctxt)
method_info = {'method': method_name,
'method_args': (instance_uuid, 2, 3),
'method_args': (instance.uuid, 2, 3),
'method_kwargs': {'arg1': 'val1', 'arg2': 'val2'}}
self.mox.StubOutWithMock(self.tgt_db_inst, 'instance_get_by_uuid')
expected_attrs = ['metadata', 'system_metadata', 'security_groups',
'info_cache']
self.tgt_db_inst.instance_get_by_uuid(self.ctxt,
instance_uuid).AndReturn('fake_instance')
@mock.patch.object(self.tgt_compute_api, method_name,
return_value='fake-result')
@mock.patch.object(objects.Instance, 'get_by_uuid',
return_value=instance)
def run_method(mock_get_by_uuid, mock_method):
response = self.src_msg_runner.run_compute_api_method(
self.ctxt,
self.tgt_cell_name,
method_info,
True)
result = response.value_or_raise()
self.assertEqual('fake-result', result)
def get_instance_mock():
# NOTE(comstud): This block of code simulates the following
# mox code:
#
# self.mox.StubOutWithMock(objects, 'Instance',
# use_mock_anything=True)
# self.mox.StubOutWithMock(objects.Instance,
# '_from_db_object')
# instance_mock = self.mox.CreateMock(objects.Instance)
# objects.Instance().AndReturn(instance_mock)
#
# Unfortunately, the above code fails on py27 do to some
# issue with the Mock object do to similar issue as this:
# https://code.google.com/p/pymox/issues/detail?id=35
#
class FakeInstance(object):
@classmethod
def _from_db_object(cls, ctxt, obj, db_obj, **kwargs):
pass
mock_get_by_uuid.assert_called_once_with(self.ctxt, instance.uuid,
expected_attrs=expected_attrs)
mock_method.assert_called_once_with(self.ctxt, instance, 2, 3,
arg1='val1', arg2='val2')
instance_mock = FakeInstance()
def fake_instance():
return instance_mock
self.stubs.Set(objects, 'Instance', fake_instance)
self.mox.StubOutWithMock(instance_mock, '_from_db_object')
return instance_mock
instance = get_instance_mock()
instance._from_db_object(self.ctxt,
instance,
'fake_instance',
expected_attrs=expected_attrs
).AndReturn(instance)
tgt_compute_api_function(self.ctxt, instance, 2, 3,
arg1='val1', arg2='val2').AndReturn('fake_result')
self.mox.ReplayAll()
response = self.src_msg_runner.run_compute_api_method(
self.ctxt,
self.tgt_cell_name,
method_info,
True)
result = response.value_or_raise()
self.assertEqual('fake_result', result)
run_method()
def test_run_compute_api_method_expects_obj(self):
# Run compute_api start method
self.mox.StubOutWithMock(self.tgt_compute_api, 'start')
self._run_compute_api_method_expects_object(self.tgt_compute_api.start,
'start')
self._run_compute_api_method('start')
def test_run_compute_api_method_expects_obj_with_info_cache(self):
def test_run_compute_api_method_shelve_with_info_cache(self):
# Run compute_api shelve method as it requires info_cache and
# metadata to be present in instance object
self.mox.StubOutWithMock(self.tgt_compute_api, 'shelve')
self._run_compute_api_method_expects_object(
self.tgt_compute_api.shelve, 'shelve',
expected_attrs=['metadata', 'info_cache'])
self._run_compute_api_method('shelve')
def test_run_compute_api_method_unknown_instance(self):
# Unknown instance should send a broadcast up that instance
# is gone.
instance_uuid = 'fake_instance_uuid'
instance = {'uuid': instance_uuid}
instance = fake_instance.fake_instance_obj(self.ctxt)
instance_uuid = instance.uuid
method_info = {'method': 'reboot',
'method_args': (instance_uuid, 2, 3),
'method_kwargs': {'arg1': 'val1', 'arg2': 'val2'}}
self.mox.StubOutWithMock(self.tgt_db_inst, 'instance_get_by_uuid')
self.mox.StubOutWithMock(objects.Instance, 'get_by_uuid')
self.mox.StubOutWithMock(self.tgt_msg_runner,
'instance_destroy_at_top')
self.tgt_db_inst.instance_get_by_uuid(self.ctxt,
'fake_instance_uuid').AndRaise(
exception.InstanceNotFound(instance_id=instance_uuid))
self.tgt_msg_runner.instance_destroy_at_top(self.ctxt, instance)
objects.Instance.get_by_uuid(self.ctxt, instance.uuid,
expected_attrs=['metadata', 'system_metadata',
'security_groups', 'info_cache']).AndRaise(
exception.InstanceNotFound(instance_id=instance_uuid))
self.tgt_msg_runner.instance_destroy_at_top(self.ctxt,
{'uuid': instance.uuid})
self.mox.ReplayAll()