diff --git a/cinder/api/contrib/volume_actions.py b/cinder/api/contrib/volume_actions.py index ae3f1f9e365..1e9cc529dbc 100644 --- a/cinder/api/contrib/volume_actions.py +++ b/cinder/api/contrib/volume_actions.py @@ -210,7 +210,11 @@ class VolumeActionsController(wsgi.Controller): connector = body['os-terminate_connection']['connector'] except KeyError: raise webob.exc.HTTPBadRequest("Must specify 'connector'") - self.volume_api.terminate_connection(context, volume, connector) + try: + self.volume_api.terminate_connection(context, volume, connector) + except exception.VolumeBackendAPIException as error: + msg = _("Unable to terminate volume connection from backend.") + raise webob.exc.HTTPInternalServerError(explanation=msg) return webob.Response(status_int=202) @wsgi.response(202) diff --git a/cinder/tests/api/contrib/test_volume_actions.py b/cinder/tests/api/contrib/test_volume_actions.py index 82f4e126d2c..1a70daffd2a 100644 --- a/cinder/tests/api/contrib/test_volume_actions.py +++ b/cinder/tests/api/contrib/test_volume_actions.py @@ -107,34 +107,44 @@ class VolumeActionsTest(test.TestCase): self.assertEqual(res.status_int, 500) def test_terminate_connection(self): - def fake_terminate_connection(*args, **kwargs): - return {} - self.stubs.Set(volume.API, 'terminate_connection', - fake_terminate_connection) + with mock.patch.object(volume_api.API, + 'terminate_connection') as terminate_conn: + terminate_conn.return_value = {} + body = {'os-terminate_connection': {'connector': 'fake'}} + req = webob.Request.blank('/v2/fake/volumes/1/action') + req.method = "POST" + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" - body = {'os-terminate_connection': {'connector': 'fake'}} - req = webob.Request.blank('/v2/fake/volumes/1/action') - req.method = "POST" - req.body = jsonutils.dumps(body) - req.headers["content-type"] = "application/json" - - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 202) + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 202) def test_terminate_connection_without_connector(self): - def fake_terminate_connection(*args, **kwargs): - return {} - self.stubs.Set(volume.API, 'terminate_connection', - fake_terminate_connection) + with mock.patch.object(volume_api.API, + 'terminate_connection') as terminate_conn: + terminate_conn.return_value = {} + body = {'os-terminate_connection': {}} + req = webob.Request.blank('/v2/fake/volumes/1/action') + req.method = "POST" + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" - body = {'os-terminate_connection': {}} - req = webob.Request.blank('/v2/fake/volumes/1/action') - req.method = "POST" - req.body = jsonutils.dumps(body) - req.headers["content-type"] = "application/json" + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 400) - res = req.get_response(fakes.wsgi_app()) - self.assertEqual(res.status_int, 400) + def test_terminate_connection_with_exception(self): + with mock.patch.object(volume_api.API, + 'terminate_connection') as terminate_conn: + terminate_conn.side_effect = \ + exception.VolumeBackendAPIException(data=None) + body = {'os-terminate_connection': {'connector': 'fake'}} + req = webob.Request.blank('/v2/fake/volumes/1/action') + req.method = "POST" + req.body = jsonutils.dumps(body) + req.headers["content-type"] = "application/json" + + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 500) def test_attach_to_instance(self): body = {'os-attach': {'instance_uuid': 'fake', diff --git a/cinder/volume/manager.py b/cinder/volume/manager.py index 9c8fef2b325..4ee29e49855 100644 --- a/cinder/volume/manager.py +++ b/cinder/volume/manager.py @@ -750,7 +750,14 @@ class VolumeManager(manager.SchedulerDependentManager): The format of connector is the same as for initialize_connection. """ volume_ref = self.db.volume_get(context, volume_id) - self.driver.terminate_connection(volume_ref, connector, force=force) + try: + self.driver.terminate_connection(volume_ref, + connector, force=force) + except Exception as err: + err_msg = (_('Unable to terminate volume connection: %(err)s') + % {'err': str(err)}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) @utils.require_driver_initialized def accept_transfer(self, context, volume_id, new_user, new_project):