]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Handle initialize_connection() exception in volume manager
authorZhiteng Huang <zhithuang@ebaysf.com>
Mon, 2 Dec 2013 09:05:08 +0000 (17:05 +0800)
committerZhiteng Huang <zhithuang@ebaysf.com>
Mon, 23 Dec 2013 17:04:26 +0000 (01:04 +0800)
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

cinder/api/contrib/volume_actions.py
cinder/tests/api/contrib/test_volume_actions.py
cinder/volume/manager.py

index abea4d96faa4305cab7f549a251a029f95e8056c..ae3f1f9e365f6b54503b9af40736b352b42f2725 100644 (file)
@@ -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')
index 153dde9404b12325e0634f5f6a5351d665f1b8ea..82f4e126d2caa7078ed9b410a941fe9741675543 100644 (file)
@@ -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):
index 7f95a88d51a7074d211508e4f5930d9a6e0eba53..9c8fef2b3255913790bfe3380c4c2325ff6b15b3 100644 (file)
@@ -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']