When the glance client throws an Exception at Horizon, catch it and determine if it is an HTTP exception. If it is, recast it as a glance_client.ClientConnectionError so that Horizon can deal with it in a better manner. Fixes 951200
Change-Id: I7cbfa74d340d25d523ab8fb18138f5cf7d33a9d9
This commit is contained in:
parent
bf217e8381
commit
7b526b6a7e
@ -20,10 +20,14 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import functools
|
||||
import logging
|
||||
import urlparse
|
||||
|
||||
from django.utils.decorators import available_attrs
|
||||
|
||||
from glance import client as glance_client
|
||||
from glance.common import exception as glance_exception
|
||||
|
||||
from horizon.api.base import APIDictWrapper, url_for
|
||||
|
||||
@ -31,6 +35,27 @@ from horizon.api.base import APIDictWrapper, url_for
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def catch_glance_exception(func):
|
||||
"""
|
||||
The glance client sometimes throws ``Exception`` classed exceptions for
|
||||
HTTP communication issues. Catch those, and rethrow them as
|
||||
``glance_client.ClientConnectionErrors`` so that we can do something
|
||||
useful with them.
|
||||
"""
|
||||
# TODO(johnp): Remove this once Bug 952618 is fixed in the glance client.
|
||||
@functools.wraps(func, assigned=available_attrs(func))
|
||||
def inner_func(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception as exc:
|
||||
exc_message = str(exc)
|
||||
if('Unknown error occurred' in exc_message or
|
||||
'Internal Server error' in exc_message):
|
||||
raise glance_exception.ClientConnectionError(exc_message)
|
||||
raise
|
||||
return inner_func
|
||||
|
||||
|
||||
class Image(APIDictWrapper):
|
||||
"""
|
||||
Wrapper around glance image dictionary to make it object-like and provide
|
||||
@ -58,6 +83,7 @@ class ImageProperties(APIDictWrapper):
|
||||
'project_id', 'ramdisk_id', 'image_type']
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def glanceclient(request):
|
||||
o = urlparse.urlparse(url_for(request, 'image'))
|
||||
LOG.debug('glanceclient connection created for host "%s:%d"' %
|
||||
@ -67,14 +93,17 @@ def glanceclient(request):
|
||||
auth_tok=request.user.token)
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def image_create(request, image_meta, image_file):
|
||||
return Image(glanceclient(request).add_image(image_meta, image_file))
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def image_delete(request, image_id):
|
||||
return glanceclient(request).delete_image(image_id)
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def image_get(request, image_id):
|
||||
"""
|
||||
Returns the actual image file from Glance for image with
|
||||
@ -83,6 +112,7 @@ def image_get(request, image_id):
|
||||
return glanceclient(request).get_image(image_id)[1]
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def image_get_meta(request, image_id):
|
||||
"""
|
||||
Returns an Image object populated with metadata for image
|
||||
@ -91,16 +121,19 @@ def image_get_meta(request, image_id):
|
||||
return Image(glanceclient(request).get_image_meta(image_id))
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def image_list_detailed(request):
|
||||
return [Image(i) for i in glanceclient(request).get_images_detailed()]
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def image_update(request, image_id, image_meta=None):
|
||||
image_meta = image_meta and image_meta or {}
|
||||
return Image(glanceclient(request).update_image(image_id,
|
||||
image_meta=image_meta))
|
||||
|
||||
|
||||
@catch_glance_exception
|
||||
def snapshot_list_detailed(request):
|
||||
filters = {}
|
||||
filters['property-image_type'] = 'snapshot'
|
||||
|
@ -18,6 +18,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from glance.common import exception as glance_exception
|
||||
|
||||
from horizon import api
|
||||
from horizon import test
|
||||
|
||||
@ -56,6 +58,35 @@ class GlanceApiTests(test.APITestCase):
|
||||
self.assertIsInstance(image, api.glance.Image)
|
||||
self.assertIn(image._apidict, images)
|
||||
|
||||
def test_glance_exception_wrapping_for_internal_server_errors(self):
|
||||
"""
|
||||
Verify that generic "Exception" classed exceptions from the glance
|
||||
client's HTTP Internal Service Errors get converted to
|
||||
ClientConnectionError's.
|
||||
"""
|
||||
# TODO(johnp): Remove once Bug 952618 is fixed in the glance client.
|
||||
glanceclient = self.stub_glanceclient()
|
||||
glanceclient.get_images_detailed().AndRaise(
|
||||
Exception("Internal Server error: "))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
with self.assertRaises(glance_exception.ClientConnectionError):
|
||||
api.image_list_detailed(self.request)
|
||||
|
||||
def test_glance_exception_wrapping_for_generic_http_errors(self):
|
||||
"""
|
||||
Verify that generic "Exception" classed exceptions from the glance
|
||||
client's HTTP errors get converted to ClientConnectionError's.
|
||||
"""
|
||||
# TODO(johnp): Remove once Bug 952618 is fixed in the glance client.
|
||||
glanceclient = self.stub_glanceclient()
|
||||
glanceclient.get_images_detailed().AndRaise(
|
||||
Exception("Unknown error occurred! 503 Service Unavailable"))
|
||||
self.mox.ReplayAll()
|
||||
|
||||
with self.assertRaises(glance_exception.ClientConnectionError):
|
||||
api.image_list_detailed(self.request)
|
||||
|
||||
|
||||
class ImageWrapperTests(test.TestCase):
|
||||
""" Tests for wrapper classes since they have extra logic attached. """
|
||||
|
Loading…
Reference in New Issue
Block a user