From: Zhiteng Huang Date: Mon, 2 Dec 2013 09:05:08 +0000 (+0800) Subject: Handle initialize_connection() exception in volume manager X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=17e556acf5c33583dcb6ed34bfd5a5fd9c148c9e;p=openstack-build%2Fcinder-build.git Handle initialize_connection() exception in volume manager Due to the fact that several drivers require backend communication to fetch connection information for a volume, it's possile that these driver throw exceptions while doing that. Currently exceptions can bubble up to volume API not being handled. This patch logs exception in volume manager and then raises VolumeBackendAPIException to caller. Change-Id: Ib3cc152e04ba029dd835a64b0cfb0a77b8a6828e Closes-bug: 1256804 --- diff --git a/cinder/api/contrib/volume_actions.py b/cinder/api/contrib/volume_actions.py index abea4d96f..ae3f1f9e3 100644 --- a/cinder/api/contrib/volume_actions.py +++ b/cinder/api/contrib/volume_actions.py @@ -188,9 +188,14 @@ class VolumeActionsController(wsgi.Controller): connector = body['os-initialize_connection']['connector'] except KeyError: raise webob.exc.HTTPBadRequest("Must specify 'connector'") - info = self.volume_api.initialize_connection(context, - volume, - connector) + try: + info = self.volume_api.initialize_connection(context, + volume, + connector) + except exception.VolumeBackendAPIException as error: + msg = _("Unable to fetch connection information from backend.") + raise webob.exc.HTTPInternalServerError(msg) + return {'connection_info': info} @wsgi.action('os-terminate_connection') diff --git a/cinder/tests/api/contrib/test_volume_actions.py b/cinder/tests/api/contrib/test_volume_actions.py index 153dde940..82f4e126d 100644 --- a/cinder/tests/api/contrib/test_volume_actions.py +++ b/cinder/tests/api/contrib/test_volume_actions.py @@ -17,6 +17,8 @@ import datetime import json import uuid + +import mock import webob from cinder.api.contrib import volume_actions @@ -65,34 +67,44 @@ class VolumeActionsTest(test.TestCase): self.assertEqual(res.status_int, 202) def test_initialize_connection(self): - def fake_initialize_connection(*args, **kwargs): - return {} - self.stubs.Set(volume.API, 'initialize_connection', - fake_initialize_connection) - - body = {'os-initialize_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" + with mock.patch.object(volume_api.API, + 'initialize_connection') as init_conn: + init_conn.return_value = {} + body = {'os-initialize_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, 200) + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 200) def test_initialize_connection_without_connector(self): - def fake_initialize_connection(*args, **kwargs): - return {} - self.stubs.Set(volume.API, 'initialize_connection', - fake_initialize_connection) + with mock.patch.object(volume_api.API, + 'initialize_connection') as init_conn: + init_conn.return_value = {} + body = {'os-initialize_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-initialize_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) + + def test_initialize_connection_exception(self): + with mock.patch.object(volume_api.API, + 'initialize_connection') as init_conn: + init_conn.side_effect = \ + exception.VolumeBackendAPIException(data=None) + body = {'os-initialize_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, 400) + res = req.get_response(fakes.wsgi_app()) + self.assertEqual(res.status_int, 500) def test_terminate_connection(self): def fake_terminate_connection(*args, **kwargs): diff --git a/cinder/volume/manager.py b/cinder/volume/manager.py index 7f95a88d5..9c8fef2b3 100644 --- a/cinder/volume/manager.py +++ b/cinder/volume/manager.py @@ -708,7 +708,13 @@ class VolumeManager(manager.SchedulerDependentManager): """ volume = self.db.volume_get(context, volume_id) self.driver.validate_connector(connector) - conn_info = self.driver.initialize_connection(volume, connector) + try: + conn_info = self.driver.initialize_connection(volume, connector) + except Exception as err: + err_msg = (_('Unable to fetch connection information from ' + 'backend: %(err)s') % {'err': str(err)}) + LOG.error(err_msg) + raise exception.VolumeBackendAPIException(data=err_msg) # Add qos_specs to connection info typeid = volume['volume_type_id']