Target cell in super conductor operations
This adds a decorator that lets us auto-target cells for methods in conductor that are supposed to run at super-conductor level and thus need to look up the instance mapping. Related to blueprint cells-aware-api Change-Id: I1c9a16b90774fb76e96b36259327dc0f273ab3c8
This commit is contained in:
parent
17fbd245cb
commit
30172f6e30
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import copy
|
import copy
|
||||||
|
import functools
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
@ -53,6 +54,35 @@ LOG = logging.getLogger(__name__)
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
|
def targets_cell(fn):
|
||||||
|
"""Wrap a method and automatically target the instance's cell.
|
||||||
|
|
||||||
|
This decorates a method with signature func(self, context, instance, ...)
|
||||||
|
and automatically targets the context with the instance's cell
|
||||||
|
mapping. It does this by looking up the InstanceMapping.
|
||||||
|
"""
|
||||||
|
@functools.wraps(fn)
|
||||||
|
def wrapper(self, context, *args, **kwargs):
|
||||||
|
instance = kwargs.get('instance') or args[0]
|
||||||
|
try:
|
||||||
|
im = objects.InstanceMapping.get_by_instance_uuid(
|
||||||
|
context, instance.uuid)
|
||||||
|
except exception.InstanceMappingNotFound:
|
||||||
|
LOG.error(_LE('InstanceMapping not found, unable to target cell'),
|
||||||
|
instance=instance)
|
||||||
|
im = None
|
||||||
|
else:
|
||||||
|
LOG.debug('Targeting cell %(cell)s for conductor method %(meth)s',
|
||||||
|
{'cell': im.cell_mapping,
|
||||||
|
'meth': fn.__name__})
|
||||||
|
# NOTE(danms): Target our context to the cell for the rest of
|
||||||
|
# this request, so that none of the subsequent code needs to
|
||||||
|
# care about it.
|
||||||
|
nova_context.set_target_cell(context, im.cell_mapping)
|
||||||
|
return fn(self, context, *args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
class ConductorManager(manager.Manager):
|
class ConductorManager(manager.Manager):
|
||||||
"""Mission: Conduct things.
|
"""Mission: Conduct things.
|
||||||
|
|
||||||
|
@ -221,6 +251,7 @@ class ComputeTaskManager(base.Base):
|
||||||
exception.MigrationPreCheckClientException,
|
exception.MigrationPreCheckClientException,
|
||||||
exception.LiveMigrationWithOldNovaNotSupported,
|
exception.LiveMigrationWithOldNovaNotSupported,
|
||||||
exception.UnsupportedPolicyException)
|
exception.UnsupportedPolicyException)
|
||||||
|
@targets_cell
|
||||||
@wrap_instance_event(prefix='conductor')
|
@wrap_instance_event(prefix='conductor')
|
||||||
def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
|
def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
|
||||||
flavor, block_migration, disk_over_commit, reservations=None,
|
flavor, block_migration, disk_over_commit, reservations=None,
|
||||||
|
@ -494,6 +525,9 @@ class ComputeTaskManager(base.Base):
|
||||||
inst_mapping.save()
|
inst_mapping.save()
|
||||||
return inst_mapping
|
return inst_mapping
|
||||||
|
|
||||||
|
# NOTE(danms): This is never cell-targeted because it is only used for
|
||||||
|
# cellsv1 (which does not target cells directly) and n-cpu reschedules
|
||||||
|
# (which go to the cell conductor and thus are always cell-specific).
|
||||||
def build_instances(self, context, instances, image, filter_properties,
|
def build_instances(self, context, instances, image, filter_properties,
|
||||||
admin_password, injected_files, requested_networks,
|
admin_password, injected_files, requested_networks,
|
||||||
security_groups, block_device_mapping=None, legacy_bdm=True):
|
security_groups, block_device_mapping=None, legacy_bdm=True):
|
||||||
|
@ -597,6 +631,7 @@ class ComputeTaskManager(base.Base):
|
||||||
hosts = self.scheduler_client.select_destinations(context, spec_obj)
|
hosts = self.scheduler_client.select_destinations(context, spec_obj)
|
||||||
return hosts
|
return hosts
|
||||||
|
|
||||||
|
@targets_cell
|
||||||
def unshelve_instance(self, context, instance, request_spec=None):
|
def unshelve_instance(self, context, instance, request_spec=None):
|
||||||
sys_meta = instance.system_metadata
|
sys_meta = instance.system_metadata
|
||||||
|
|
||||||
|
@ -686,6 +721,7 @@ class ComputeTaskManager(base.Base):
|
||||||
instance.save()
|
instance.save()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@targets_cell
|
||||||
def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
|
def rebuild_instance(self, context, instance, orig_image_ref, image_ref,
|
||||||
injected_files, new_pass, orig_sys_metadata,
|
injected_files, new_pass, orig_sys_metadata,
|
||||||
bdms, recreate, on_shared_storage,
|
bdms, recreate, on_shared_storage,
|
||||||
|
|
|
@ -331,13 +331,16 @@ class _BaseTaskTestCase(object):
|
||||||
|
|
||||||
return rebuild_args, compute_rebuild_args
|
return rebuild_args, compute_rebuild_args
|
||||||
|
|
||||||
|
@mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
|
||||||
@mock.patch.object(objects.RequestSpec, 'save')
|
@mock.patch.object(objects.RequestSpec, 'save')
|
||||||
@mock.patch.object(migrate.MigrationTask, 'execute')
|
@mock.patch.object(migrate.MigrationTask, 'execute')
|
||||||
@mock.patch.object(utils, 'get_image_from_system_metadata')
|
@mock.patch.object(utils, 'get_image_from_system_metadata')
|
||||||
@mock.patch.object(objects.RequestSpec, 'from_components')
|
@mock.patch.object(objects.RequestSpec, 'from_components')
|
||||||
def _test_cold_migrate(self, spec_from_components, get_image_from_metadata,
|
def _test_cold_migrate(self, spec_from_components, get_image_from_metadata,
|
||||||
migration_task_execute, spec_save,
|
migration_task_execute, spec_save, get_im,
|
||||||
clean_shutdown=True):
|
clean_shutdown=True):
|
||||||
|
get_im.return_value.cell_mapping = (
|
||||||
|
objects.CellMappingList.get_all(self.context)[0])
|
||||||
get_image_from_metadata.return_value = 'image'
|
get_image_from_metadata.return_value = 'image'
|
||||||
inst = fake_instance.fake_db_instance(image_ref='image_ref')
|
inst = fake_instance.fake_db_instance(image_ref='image_ref')
|
||||||
inst_obj = objects.Instance._from_db_object(
|
inst_obj = objects.Instance._from_db_object(
|
||||||
|
@ -1687,13 +1690,20 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||||
self.assertNotEqual(old_rpcapi,
|
self.assertNotEqual(old_rpcapi,
|
||||||
self.conductor_manager.compute_rpcapi)
|
self.conductor_manager.compute_rpcapi)
|
||||||
|
|
||||||
def test_migrate_server_fails_with_rebuild(self):
|
@mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
|
||||||
|
def test_migrate_server_fails_with_rebuild(self, get_im):
|
||||||
|
get_im.return_value.cell_mapping = (
|
||||||
|
objects.CellMappingList.get_all(self.context)[0])
|
||||||
|
|
||||||
instance = fake_instance.fake_instance_obj(self.context,
|
instance = fake_instance.fake_instance_obj(self.context,
|
||||||
vm_state=vm_states.ACTIVE)
|
vm_state=vm_states.ACTIVE)
|
||||||
self.assertRaises(NotImplementedError, self.conductor.migrate_server,
|
self.assertRaises(NotImplementedError, self.conductor.migrate_server,
|
||||||
self.context, instance, None, True, True, None, None, None)
|
self.context, instance, None, True, True, None, None, None)
|
||||||
|
|
||||||
def test_migrate_server_fails_with_flavor(self):
|
@mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
|
||||||
|
def test_migrate_server_fails_with_flavor(self, get_im):
|
||||||
|
get_im.return_value.cell_mapping = (
|
||||||
|
objects.CellMappingList.get_all(self.context)[0])
|
||||||
flavor = flavors.get_flavor_by_name('m1.tiny')
|
flavor = flavors.get_flavor_by_name('m1.tiny')
|
||||||
instance = fake_instance.fake_instance_obj(self.context,
|
instance = fake_instance.fake_instance_obj(self.context,
|
||||||
vm_state=vm_states.ACTIVE,
|
vm_state=vm_states.ACTIVE,
|
||||||
|
@ -1707,10 +1717,13 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||||
'uuid': instance['uuid'], },
|
'uuid': instance['uuid'], },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
|
||||||
@mock.patch.object(scheduler_utils, 'set_vm_state_and_notify')
|
@mock.patch.object(scheduler_utils, 'set_vm_state_and_notify')
|
||||||
@mock.patch.object(live_migrate.LiveMigrationTask, 'execute')
|
@mock.patch.object(live_migrate.LiveMigrationTask, 'execute')
|
||||||
def _test_migrate_server_deals_with_expected_exceptions(self, ex,
|
def _test_migrate_server_deals_with_expected_exceptions(self, ex,
|
||||||
mock_execute, mock_set):
|
mock_execute, mock_set, get_im):
|
||||||
|
get_im.return_value.cell_mapping = (
|
||||||
|
objects.CellMappingList.get_all(self.context)[0])
|
||||||
instance = fake_instance.fake_db_instance(uuid=uuids.instance,
|
instance = fake_instance.fake_db_instance(uuid=uuids.instance,
|
||||||
vm_state=vm_states.ACTIVE)
|
vm_state=vm_states.ACTIVE)
|
||||||
inst_obj = objects.Instance._from_db_object(
|
inst_obj = objects.Instance._from_db_object(
|
||||||
|
@ -1731,7 +1744,10 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||||
'expected_task_state': task_states.MIGRATING},
|
'expected_task_state': task_states.MIGRATING},
|
||||||
ex, self._build_request_spec(inst_obj))
|
ex, self._build_request_spec(inst_obj))
|
||||||
|
|
||||||
def test_migrate_server_deals_with_invalidcpuinfo_exception(self):
|
@mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
|
||||||
|
def test_migrate_server_deals_with_invalidcpuinfo_exception(self, get_im):
|
||||||
|
get_im.return_value.cell_mapping = (
|
||||||
|
objects.CellMappingList.get_all(self.context)[0])
|
||||||
instance = fake_instance.fake_db_instance(uuid=uuids.instance,
|
instance = fake_instance.fake_db_instance(uuid=uuids.instance,
|
||||||
vm_state=vm_states.ACTIVE)
|
vm_state=vm_states.ACTIVE)
|
||||||
inst_obj = objects.Instance._from_db_object(
|
inst_obj = objects.Instance._from_db_object(
|
||||||
|
@ -1783,10 +1799,13 @@ class ConductorTaskTestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||||
for ex in exs:
|
for ex in exs:
|
||||||
self._test_migrate_server_deals_with_expected_exceptions(ex)
|
self._test_migrate_server_deals_with_expected_exceptions(ex)
|
||||||
|
|
||||||
|
@mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
|
||||||
@mock.patch.object(scheduler_utils, 'set_vm_state_and_notify')
|
@mock.patch.object(scheduler_utils, 'set_vm_state_and_notify')
|
||||||
@mock.patch.object(live_migrate.LiveMigrationTask, 'execute')
|
@mock.patch.object(live_migrate.LiveMigrationTask, 'execute')
|
||||||
def test_migrate_server_deals_with_unexpected_exceptions(self,
|
def test_migrate_server_deals_with_unexpected_exceptions(self,
|
||||||
mock_live_migrate, mock_set_state):
|
mock_live_migrate, mock_set_state, get_im):
|
||||||
|
get_im.return_value.cell_mapping = (
|
||||||
|
objects.CellMappingList.get_all(self.context)[0])
|
||||||
expected_ex = IOError('fake error')
|
expected_ex = IOError('fake error')
|
||||||
mock_live_migrate.side_effect = expected_ex
|
mock_live_migrate.side_effect = expected_ex
|
||||||
instance = fake_instance.fake_db_instance()
|
instance = fake_instance.fake_db_instance()
|
||||||
|
@ -2323,6 +2342,20 @@ class ConductorTaskRPCAPITestCase(_BaseTaskTestCase,
|
||||||
self.context, 'live_migrate_instance', **kw)
|
self.context, 'live_migrate_instance', **kw)
|
||||||
_test()
|
_test()
|
||||||
|
|
||||||
|
@mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid')
|
||||||
|
def test_targets_cell_no_instance_mapping(self, mock_im):
|
||||||
|
|
||||||
|
@conductor_manager.targets_cell
|
||||||
|
def test(self, context, instance):
|
||||||
|
return mock.sentinel.iransofaraway
|
||||||
|
|
||||||
|
mock_im.side_effect = exc.InstanceMappingNotFound
|
||||||
|
ctxt = mock.MagicMock()
|
||||||
|
inst = mock.MagicMock()
|
||||||
|
self.assertEqual(mock.sentinel.iransofaraway,
|
||||||
|
test(None, ctxt, inst))
|
||||||
|
mock_im.assert_called_once_with(ctxt, inst.uuid)
|
||||||
|
|
||||||
|
|
||||||
class ConductorTaskAPITestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
class ConductorTaskAPITestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||||
"""Compute task API Tests."""
|
"""Compute task API Tests."""
|
||||||
|
|
Loading…
Reference in New Issue