Handle terminate_connection() exception in volume manager

Due to the fact that we sometimes need to manually terminate a volume's
connection through volume api, it's possile that these backend drivers
throw exceptions while doing that. Currently exceptions are bubbled up to
volume API not being handled. This patch logs exception in volume manager
and then raises VolumeBackendAPIException to caller.

Change-Id: If809f97998f52516af09ec21b3052b67d3a62f36
Closes-bug: #1263820
This commit is contained in:
ling-yun 2013-12-24 11:45:16 +08:00
parent 8a5ce963dc
commit 1e73e64e74
3 changed files with 48 additions and 25 deletions

View File

@ -205,7 +205,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)

View File

@ -17,6 +17,8 @@
import datetime
import json
import uuid
import mock
import webob
from cinder.api.contrib import volume_actions
@ -95,34 +97,44 @@ class VolumeActionsTest(test.TestCase):
self.assertEqual(res.status_int, 400)
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',

View File

@ -744,7 +744,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):