Merge "Make live migration checks async"
This commit is contained in:
commit
6bec93d862
@ -3225,7 +3225,7 @@ class API(base.Base):
|
|||||||
@check_instance_cell
|
@check_instance_cell
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.PAUSED])
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.PAUSED])
|
||||||
def live_migrate(self, context, instance, block_migration,
|
def live_migrate(self, context, instance, block_migration,
|
||||||
disk_over_commit, host_name, force=None):
|
disk_over_commit, host_name, force=None, async=False):
|
||||||
"""Migrate a server lively to a new host."""
|
"""Migrate a server lively to a new host."""
|
||||||
LOG.debug("Going to try to live migrate instance to %s",
|
LOG.debug("Going to try to live migrate instance to %s",
|
||||||
host_name or "another host", instance=instance)
|
host_name or "another host", instance=instance)
|
||||||
@ -3270,7 +3270,7 @@ class API(base.Base):
|
|||||||
self.compute_task_api.live_migrate_instance(context, instance,
|
self.compute_task_api.live_migrate_instance(context, instance,
|
||||||
host_name, block_migration=block_migration,
|
host_name, block_migration=block_migration,
|
||||||
disk_over_commit=disk_over_commit,
|
disk_over_commit=disk_over_commit,
|
||||||
request_spec=request_spec)
|
request_spec=request_spec, async=async)
|
||||||
except oslo_exceptions.MessagingTimeout as messaging_timeout:
|
except oslo_exceptions.MessagingTimeout as messaging_timeout:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
# NOTE(pkoniszewski): It is possible that MessagingTimeout
|
# NOTE(pkoniszewski): It is possible that MessagingTimeout
|
||||||
|
@ -79,9 +79,9 @@ class LocalComputeTaskAPI(object):
|
|||||||
block_migration, disk_over_commit,
|
block_migration, disk_over_commit,
|
||||||
request_spec=None):
|
request_spec=None):
|
||||||
scheduler_hint = {'host': host_name}
|
scheduler_hint = {'host': host_name}
|
||||||
self._manager.migrate_server(
|
self._manager.live_migrate_instance(context, instance, scheduler_hint,
|
||||||
context, instance, scheduler_hint, True, False, None,
|
block_migration, disk_over_commit,
|
||||||
block_migration, disk_over_commit, None, request_spec=request_spec)
|
request_spec)
|
||||||
|
|
||||||
def build_instances(self, context, instances, image,
|
def build_instances(self, context, instances, image,
|
||||||
filter_properties, admin_password, injected_files,
|
filter_properties, admin_password, injected_files,
|
||||||
@ -189,11 +189,17 @@ class ComputeTaskAPI(object):
|
|||||||
|
|
||||||
def live_migrate_instance(self, context, instance, host_name,
|
def live_migrate_instance(self, context, instance, host_name,
|
||||||
block_migration, disk_over_commit,
|
block_migration, disk_over_commit,
|
||||||
request_spec=None):
|
request_spec=None, async=False):
|
||||||
scheduler_hint = {'host': host_name}
|
scheduler_hint = {'host': host_name}
|
||||||
self.conductor_compute_rpcapi.migrate_server(
|
if async:
|
||||||
context, instance, scheduler_hint, True, False, None,
|
self.conductor_compute_rpcapi.live_migrate_instance(
|
||||||
block_migration, disk_over_commit, None, request_spec=request_spec)
|
context, instance, scheduler_hint, block_migration,
|
||||||
|
disk_over_commit, request_spec)
|
||||||
|
else:
|
||||||
|
self.conductor_compute_rpcapi.migrate_server(
|
||||||
|
context, instance, scheduler_hint, True, False, None,
|
||||||
|
block_migration, disk_over_commit, None,
|
||||||
|
request_spec=request_spec)
|
||||||
|
|
||||||
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,
|
||||||
|
@ -145,7 +145,7 @@ class ComputeTaskManager(base.Base):
|
|||||||
may involve coordinating activities on multiple compute nodes.
|
may involve coordinating activities on multiple compute nodes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
target = messaging.Target(namespace='compute_task', version='1.14')
|
target = messaging.Target(namespace='compute_task', version='1.15')
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(ComputeTaskManager, self).__init__()
|
super(ComputeTaskManager, self).__init__()
|
||||||
@ -161,6 +161,8 @@ class ComputeTaskManager(base.Base):
|
|||||||
compute_rpcapi.LAST_VERSION = None
|
compute_rpcapi.LAST_VERSION = None
|
||||||
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
|
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
|
||||||
|
|
||||||
|
# TODO(tdurakov): remove `live` parameter here on compute task api RPC
|
||||||
|
# version bump to 2.x
|
||||||
@messaging.expected_exceptions(
|
@messaging.expected_exceptions(
|
||||||
exception.NoValidHost,
|
exception.NoValidHost,
|
||||||
exception.ComputeServiceUnavailable,
|
exception.ComputeServiceUnavailable,
|
||||||
@ -288,6 +290,12 @@ class ComputeTaskManager(base.Base):
|
|||||||
# exception will be raised by instance.save()
|
# exception will be raised by instance.save()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@wrap_instance_event(prefix='conductor')
|
||||||
|
def live_migrate_instance(self, context, instance, scheduler_hint,
|
||||||
|
block_migration, disk_over_commit, request_spec):
|
||||||
|
self._live_migrate(context, instance, scheduler_hint,
|
||||||
|
block_migration, disk_over_commit, request_spec)
|
||||||
|
|
||||||
def _live_migrate(self, context, instance, scheduler_hint,
|
def _live_migrate(self, context, instance, scheduler_hint,
|
||||||
block_migration, disk_over_commit, request_spec):
|
block_migration, disk_over_commit, request_spec):
|
||||||
destination = scheduler_hint.get("host")
|
destination = scheduler_hint.get("host")
|
||||||
|
@ -266,6 +266,7 @@ class ComputeTaskAPI(object):
|
|||||||
1.12 - Added request_spec to rebuild_instance()
|
1.12 - Added request_spec to rebuild_instance()
|
||||||
1.13 - Added request_spec to migrate_server()
|
1.13 - Added request_spec to migrate_server()
|
||||||
1.14 - Added request_spec to unshelve_instance()
|
1.14 - Added request_spec to unshelve_instance()
|
||||||
|
1.15 - Added live_migrate_instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -276,6 +277,17 @@ class ComputeTaskAPI(object):
|
|||||||
serializer = objects_base.NovaObjectSerializer()
|
serializer = objects_base.NovaObjectSerializer()
|
||||||
self.client = rpc.get_client(target, serializer=serializer)
|
self.client = rpc.get_client(target, serializer=serializer)
|
||||||
|
|
||||||
|
def live_migrate_instance(self, context, instance, scheduler_hint,
|
||||||
|
block_migration, disk_over_commit, request_spec):
|
||||||
|
kw = {'instance': instance, 'scheduler_hint': scheduler_hint,
|
||||||
|
'block_migration': block_migration,
|
||||||
|
'disk_over_commit': disk_over_commit,
|
||||||
|
'request_spec': request_spec,
|
||||||
|
}
|
||||||
|
version = '1.15'
|
||||||
|
cctxt = self.client.prepare(version=version)
|
||||||
|
cctxt.cast(context, 'live_migrate_instance', **kw)
|
||||||
|
|
||||||
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,
|
flavor, block_migration, disk_over_commit,
|
||||||
reservations=None, clean_shutdown=True, request_spec=None):
|
reservations=None, clean_shutdown=True, request_spec=None):
|
||||||
|
@ -9940,7 +9940,7 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
block_migration=True,
|
block_migration=True,
|
||||||
disk_over_commit=True,
|
disk_over_commit=True,
|
||||||
host_name='fake_dest_host',
|
host_name='fake_dest_host',
|
||||||
force=force)
|
force=force, async=False)
|
||||||
|
|
||||||
record_action_start.assert_called_once_with(self.context, instance,
|
record_action_start.assert_called_once_with(self.context, instance,
|
||||||
'live-migration')
|
'live-migration')
|
||||||
@ -9952,7 +9952,7 @@ class ComputeAPITestCase(BaseTestCase):
|
|||||||
self.context, instance, host,
|
self.context, instance, host,
|
||||||
block_migration=True,
|
block_migration=True,
|
||||||
disk_over_commit=True,
|
disk_over_commit=True,
|
||||||
request_spec=fake_spec)
|
request_spec=fake_spec, async=False)
|
||||||
|
|
||||||
do_test()
|
do_test()
|
||||||
instance.refresh()
|
instance.refresh()
|
||||||
|
@ -1970,7 +1970,8 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
'fake_dest_host',
|
'fake_dest_host',
|
||||||
block_migration=True,
|
block_migration=True,
|
||||||
disk_over_commit=True,
|
disk_over_commit=True,
|
||||||
request_spec=fake_spec)
|
request_spec=fake_spec,
|
||||||
|
async=False)
|
||||||
|
|
||||||
def test_swap_volume_volume_api_usage(self):
|
def test_swap_volume_volume_api_usage(self):
|
||||||
# This test ensures that volume_id arguments are passed to volume_api
|
# This test ensures that volume_id arguments are passed to volume_api
|
||||||
|
@ -326,35 +326,6 @@ class _BaseTaskTestCase(object):
|
|||||||
|
|
||||||
return rebuild_args, compute_rebuild_args
|
return rebuild_args, compute_rebuild_args
|
||||||
|
|
||||||
@mock.patch('nova.objects.Migration')
|
|
||||||
def test_live_migrate(self, migobj):
|
|
||||||
inst = fake_instance.fake_db_instance()
|
|
||||||
inst_obj = objects.Instance._from_db_object(
|
|
||||||
self.context, objects.Instance(), inst, [])
|
|
||||||
|
|
||||||
migration = migobj()
|
|
||||||
self.mox.StubOutWithMock(live_migrate.LiveMigrationTask, 'execute')
|
|
||||||
task = self.conductor_manager._build_live_migrate_task(
|
|
||||||
self.context, inst_obj, 'destination', 'block_migration',
|
|
||||||
'disk_over_commit', migration)
|
|
||||||
task.execute()
|
|
||||||
self.mox.ReplayAll()
|
|
||||||
|
|
||||||
if isinstance(self.conductor, (conductor_api.ComputeTaskAPI,
|
|
||||||
conductor_api.LocalComputeTaskAPI)):
|
|
||||||
# The API method is actually 'live_migrate_instance'. It gets
|
|
||||||
# converted into 'migrate_server' when doing RPC.
|
|
||||||
self.conductor.live_migrate_instance(self.context, inst_obj,
|
|
||||||
'destination', 'block_migration', 'disk_over_commit')
|
|
||||||
else:
|
|
||||||
self.conductor.migrate_server(self.context, inst_obj,
|
|
||||||
{'host': 'destination'}, True, False, None,
|
|
||||||
'block_migration', 'disk_over_commit')
|
|
||||||
|
|
||||||
self.assertEqual('accepted', migration.status)
|
|
||||||
self.assertEqual('destination', migration.dest_compute)
|
|
||||||
self.assertEqual(inst_obj.host, migration.source_compute)
|
|
||||||
|
|
||||||
@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')
|
||||||
@ -1871,6 +1842,31 @@ class ConductorTaskRPCAPITestCase(_BaseTaskTestCase,
|
|||||||
service_manager = self.conductor_service.manager
|
service_manager = self.conductor_service.manager
|
||||||
self.conductor_manager = service_manager.compute_task_mgr
|
self.conductor_manager = service_manager.compute_task_mgr
|
||||||
|
|
||||||
|
def test_live_migrate_instance(self):
|
||||||
|
|
||||||
|
inst = fake_instance.fake_db_instance()
|
||||||
|
inst_obj = objects.Instance._from_db_object(
|
||||||
|
self.context, objects.Instance(), inst, [])
|
||||||
|
version = '1.15'
|
||||||
|
scheduler_hint = {'host': 'destination'}
|
||||||
|
cctxt_mock = mock.MagicMock()
|
||||||
|
|
||||||
|
@mock.patch.object(self.conductor.client, 'prepare',
|
||||||
|
return_value=cctxt_mock)
|
||||||
|
def _test(prepare_mock):
|
||||||
|
self.conductor.live_migrate_instance(
|
||||||
|
self.context, inst_obj, scheduler_hint,
|
||||||
|
'block_migration', 'disk_over_commit', request_spec=None)
|
||||||
|
prepare_mock.assert_called_once_with(version=version)
|
||||||
|
kw = {'instance': inst_obj, 'scheduler_hint': scheduler_hint,
|
||||||
|
'block_migration': 'block_migration',
|
||||||
|
'disk_over_commit': 'disk_over_commit',
|
||||||
|
'request_spec': None,
|
||||||
|
}
|
||||||
|
cctxt_mock.cast.assert_called_once_with(
|
||||||
|
self.context, 'live_migrate_instance', **kw)
|
||||||
|
_test()
|
||||||
|
|
||||||
|
|
||||||
class ConductorTaskAPITestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
class ConductorTaskAPITestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
||||||
"""Compute task API Tests."""
|
"""Compute task API Tests."""
|
||||||
@ -1882,6 +1878,20 @@ class ConductorTaskAPITestCase(_BaseTaskTestCase, test_compute.BaseTestCase):
|
|||||||
service_manager = self.conductor_service.manager
|
service_manager = self.conductor_service.manager
|
||||||
self.conductor_manager = service_manager.compute_task_mgr
|
self.conductor_manager = service_manager.compute_task_mgr
|
||||||
|
|
||||||
|
def test_live_migrate(self):
|
||||||
|
inst = fake_instance.fake_db_instance()
|
||||||
|
inst_obj = objects.Instance._from_db_object(
|
||||||
|
self.context, objects.Instance(), inst, [])
|
||||||
|
|
||||||
|
with mock.patch.object(self.conductor.conductor_compute_rpcapi,
|
||||||
|
'migrate_server') as mock_migrate_server:
|
||||||
|
self.conductor.live_migrate_instance(self.context, inst_obj,
|
||||||
|
'destination', 'block_migration', 'disk_over_commit')
|
||||||
|
mock_migrate_server.assert_called_once_with(
|
||||||
|
self.context, inst_obj, {'host': 'destination'}, True, False,
|
||||||
|
None, 'block_migration', 'disk_over_commit', None,
|
||||||
|
request_spec=None)
|
||||||
|
|
||||||
|
|
||||||
class ConductorLocalComputeTaskAPITestCase(ConductorTaskAPITestCase):
|
class ConductorLocalComputeTaskAPITestCase(ConductorTaskAPITestCase):
|
||||||
"""Conductor LocalComputeTaskAPI Tests."""
|
"""Conductor LocalComputeTaskAPI Tests."""
|
||||||
@ -1889,3 +1899,26 @@ class ConductorLocalComputeTaskAPITestCase(ConductorTaskAPITestCase):
|
|||||||
super(ConductorLocalComputeTaskAPITestCase, self).setUp()
|
super(ConductorLocalComputeTaskAPITestCase, self).setUp()
|
||||||
self.conductor = conductor_api.LocalComputeTaskAPI()
|
self.conductor = conductor_api.LocalComputeTaskAPI()
|
||||||
self.conductor_manager = self.conductor._manager._target
|
self.conductor_manager = self.conductor._manager._target
|
||||||
|
|
||||||
|
@mock.patch('nova.objects.Migration')
|
||||||
|
def test_live_migrate(self, migobj):
|
||||||
|
inst = fake_instance.fake_db_instance()
|
||||||
|
inst_obj = objects.Instance._from_db_object(
|
||||||
|
self.context, objects.Instance(), inst, [])
|
||||||
|
|
||||||
|
migration = migobj()
|
||||||
|
task = mock.MagicMock()
|
||||||
|
with mock.patch.object(self.conductor_manager,
|
||||||
|
'_build_live_migrate_task',
|
||||||
|
return_value=task) as mock_build_task:
|
||||||
|
self.conductor.live_migrate_instance(self.context, inst_obj,
|
||||||
|
'destination', 'block_migration', 'disk_over_commit')
|
||||||
|
mock_build_task.assert_called_once_with(self.context, inst_obj,
|
||||||
|
'destination',
|
||||||
|
'block_migration',
|
||||||
|
'disk_over_commit',
|
||||||
|
migration, None)
|
||||||
|
task.execute.assert_called_once()
|
||||||
|
self.assertEqual('accepted', migration.status)
|
||||||
|
self.assertEqual('destination', migration.dest_compute)
|
||||||
|
self.assertEqual(inst_obj.host, migration.source_compute)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user