diff --git a/nova/api/openstack/compute/shelve.py b/nova/api/openstack/compute/shelve.py index a606cdda9d04..10fc66fc4b3f 100644 --- a/nova/api/openstack/compute/shelve.py +++ b/nova/api/openstack/compute/shelve.py @@ -47,7 +47,8 @@ class ShelveController(wsgi.Controller): self.compute_api.shelve(context, instance) except exception.InstanceUnknownCell as e: raise exc.HTTPNotFound(explanation=e.format_message()) - except exception.InstanceIsLocked as e: + except (exception.InstanceIsLocked, + exception.UnexpectedTaskStateError) as e: raise exc.HTTPConflict(explanation=e.format_message()) except exception.InstanceInvalidState as state_error: common.raise_http_conflict_for_instance_invalid_state(state_error, diff --git a/nova/tests/unit/api/openstack/compute/test_shelve.py b/nova/tests/unit/api/openstack/compute/test_shelve.py index 257bc5366daa..d771a213abd6 100644 --- a/nova/tests/unit/api/openstack/compute/test_shelve.py +++ b/nova/tests/unit/api/openstack/compute/test_shelve.py @@ -15,9 +15,12 @@ import mock from oslo_policy import policy as oslo_policy from oslo_utils.fixture import uuidsentinel +import six import webob from nova.api.openstack.compute import shelve as shelve_v21 +from nova.compute import task_states +from nova.compute import vm_states from nova import exception from nova import policy from nova import test @@ -42,6 +45,22 @@ class ShelvePolicyTestV21(test.NoDBTestCase): self.assertRaises(webob.exc.HTTPConflict, self.controller._shelve, self.req, uuidsentinel.fake, {}) + @mock.patch('nova.api.openstack.common.get_instance') + @mock.patch('nova.objects.instance.Instance.save') + def test_shelve_task_state_race(self, mock_save, get_instance_mock): + instance = fake_instance.fake_instance_obj( + self.req.environ['nova.context'], + vm_state=vm_states.ACTIVE, task_state=None) + instance.launched_at = instance.created_at + get_instance_mock.return_value = instance + mock_save.side_effect = exception.UnexpectedTaskStateError( + instance_uuid=instance.uuid, expected=None, + actual=task_states.SHELVING) + ex = self.assertRaises(webob.exc.HTTPConflict, self.controller._shelve, + self.req, uuidsentinel.fake, body={'shelve': {}}) + self.assertIn('Conflict updating instance', six.text_type(ex)) + mock_save.assert_called_once_with(expected_task_state=[None]) + @mock.patch('nova.api.openstack.common.get_instance') def test_unshelve_locked_server(self, get_instance_mock): get_instance_mock.return_value = (