From 5f46f6acf3a000c8e65ee6f2574aff518b036d29 Mon Sep 17 00:00:00 2001 From: Fei Long Wang Date: Sun, 10 Mar 2013 22:57:13 +0800 Subject: Fixes instance task_state being left as migrating In a cross-hypervisor environment, such as KVM + Hyper-v. After negative test which live migrate a KVM instance to Hyper-v host, there will be an exception InvalidHypervisorType. As a result, the instance task_state is being left as migrating. That leaves the instance in a broken state and most of the requests on the instance will fail. Actually, there are some other exceptions will cause this issue as well, such as UnableToMigrateToSelf, DestinationHypervisorTooOld. All of them are thrown from the driver of scheduler during live migration. But obviously, if the live migration fails at the schedule stage, the task_state of the target instance should be updated to None. Fixes Bug: 1153283 Change-Id: Ide4b48653268c8dbe31de6964814830d2e82a5ba --- .../api/openstack/compute/contrib/admin_actions.py | 5 +- nova/scheduler/manager.py | 5 +- .../compute/contrib/test_admin_actions.py | 114 +++++++++++++++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) (limited to 'nova') diff --git a/nova/api/openstack/compute/contrib/admin_actions.py b/nova/api/openstack/compute/contrib/admin_actions.py index 8e5863706..951bc0c11 100644 --- a/nova/api/openstack/compute/contrib/admin_actions.py +++ b/nova/api/openstack/compute/contrib/admin_actions.py @@ -282,7 +282,10 @@ class AdminActionsController(wsgi.Controller): instance = self.compute_api.get(context, id) self.compute_api.live_migrate(context, instance, block_migration, disk_over_commit, host) - except exception.ComputeServiceUnavailable as ex: + except (exception.ComputeServiceUnavailable, + exception.InvalidHypervisorType, + exception.UnableToMigrateToSelf, + exception.DestinationHypervisorTooOld) as ex: raise exc.HTTPBadRequest(explanation=str(ex)) except Exception: if host is None: diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index 11798ca04..c3022fdea 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -95,7 +95,10 @@ class SchedulerManager(manager.Manager): return self.driver.schedule_live_migration( context, instance, dest, block_migration, disk_over_commit) - except exception.ComputeServiceUnavailable as ex: + except (exception.ComputeServiceUnavailable, + exception.InvalidHypervisorType, + exception.UnableToMigrateToSelf, + exception.DestinationHypervisorTooOld) as ex: request_spec = {'instance_properties': { 'uuid': instance['uuid'], }, } diff --git a/nova/tests/api/openstack/compute/contrib/test_admin_actions.py b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py index bb3cbf086..2efb6fe5a 100644 --- a/nova/tests/api/openstack/compute/contrib/test_admin_actions.py +++ b/nova/tests/api/openstack/compute/contrib/test_admin_actions.py @@ -211,6 +211,120 @@ class AdminActionsTest(test.TestCase): unicode(exception.ComputeServiceUnavailable(host='host')), res.body) + def test_migrate_live_invalid_hypervisor_type(self): + ctxt = context.get_admin_context() + ctxt.user_id = 'fake' + ctxt.project_id = 'fake' + ctxt.is_admin = True + app = fakes.wsgi_app(fake_auth_context=ctxt, init_only=('servers',)) + req = webob.Request.blank('/v2/fake/servers/%s/action' % self.UUID) + req.method = 'POST' + req.body = jsonutils.dumps({ + 'os-migrateLive': { + 'host': 'hostname', + 'block_migration': False, + 'disk_over_commit': False, + } + }) + req.content_type = 'application/json' + + def fake_update(inst, context, instance, + task_state, expected_task_state): + return None + + def fake_scheduler_api_live_migration(context, dest, + block_migration=False, + disk_over_commit=False, instance=None, + instance_id=None, topic=None): + raise exception.InvalidHypervisorType() + + self.stubs.Set(compute_api.API, 'update', fake_update) + self.stubs.Set(scheduler_rpcapi.SchedulerAPI, + 'live_migration', + fake_scheduler_api_live_migration) + + res = req.get_response(app) + self.assertEqual(res.status_int, 400) + self.assertIn( + unicode(exception.InvalidHypervisorType()), + res.body) + + def test_migrate_live_unable_to_migrate_to_self(self): + ctxt = context.get_admin_context() + ctxt.user_id = 'fake' + ctxt.project_id = 'fake' + ctxt.is_admin = True + app = fakes.wsgi_app(fake_auth_context=ctxt, init_only=('servers',)) + req = webob.Request.blank('/v2/fake/servers/%s/action' % self.UUID) + req.method = 'POST' + req.body = jsonutils.dumps({ + 'os-migrateLive': { + 'host': 'hostname', + 'block_migration': False, + 'disk_over_commit': False, + } + }) + req.content_type = 'application/json' + + def fake_update(inst, context, instance, + task_state, expected_task_state): + return None + + def fake_scheduler_api_live_migration(context, dest, + block_migration=False, + disk_over_commit=False, instance=None, + instance_id=None, topic=None): + raise exception.UnableToMigrateToSelf(self.UUID, host='host') + + self.stubs.Set(compute_api.API, 'update', fake_update) + self.stubs.Set(scheduler_rpcapi.SchedulerAPI, + 'live_migration', + fake_scheduler_api_live_migration) + + res = req.get_response(app) + self.assertEqual(res.status_int, 400) + self.assertIn( + unicode(exception.UnableToMigrateToSelf(self.UUID, host='host')), + res.body) + + def test_migrate_live_destination_hypervisor_too_old(self): + ctxt = context.get_admin_context() + ctxt.user_id = 'fake' + ctxt.project_id = 'fake' + ctxt.is_admin = True + app = fakes.wsgi_app(fake_auth_context=ctxt, init_only=('servers',)) + req = webob.Request.blank('/v2/fake/servers/%s/action' % self.UUID) + req.method = 'POST' + req.body = jsonutils.dumps({ + 'os-migrateLive': { + 'host': 'hostname', + 'block_migration': False, + 'disk_over_commit': False, + } + }) + req.content_type = 'application/json' + + def fake_update(inst, context, instance, + task_state, expected_task_state): + return None + + def fake_scheduler_api_live_migration(context, dest, + block_migration=False, + disk_over_commit=False, instance=None, + instance_id=None, topic=None): + raise exception.DestinationHypervisorTooOld() + + self.stubs.Set(compute_api.API, 'update', fake_update) + self.stubs.Set(scheduler_rpcapi.SchedulerAPI, + 'live_migration', + fake_scheduler_api_live_migration) + + res = req.get_response(app) + self.assertEqual(res.status_int, 400) + self.assertIn( + unicode(exception.DestinationHypervisorTooOld()), + res.body) + class CreateBackupTests(test.TestCase): -- cgit