summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFei Long Wang <flwang@cn.ibm.com>2013-03-10 22:57:13 +0800
committerFei Long Wang <flwang@cn.ibm.com>2013-03-12 08:16:58 +0800
commit5f46f6acf3a000c8e65ee6f2574aff518b036d29 (patch)
treeb096de6406ef8344883b5adf469e0e29061b956c
parent67069a3d1a4174fae987c8e3a7091b6fd37ff606 (diff)
downloadnova-5f46f6acf3a000c8e65ee6f2574aff518b036d29.tar.gz
nova-5f46f6acf3a000c8e65ee6f2574aff518b036d29.tar.xz
nova-5f46f6acf3a000c8e65ee6f2574aff518b036d29.zip
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
-rw-r--r--nova/api/openstack/compute/contrib/admin_actions.py5
-rw-r--r--nova/scheduler/manager.py5
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_admin_actions.py114
3 files changed, 122 insertions, 2 deletions
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):