Fixes LP Bug#862664 - Improper calls to get_image

The glance.client.Client.get_image() call returns a
tuple of (metadata, image_iterator). Unfortunately,
Horizon's glance API calls get_image() when it means
to call get_image_meta(). Because the call to get_image()
simply ignores the image iterator returned from get_image(),
when the image iterator is garbage-collected, this causes
the connection to Glance to be closed, however by that time
the socket bound to the iterator has been switched out by
eventlet. The result is lots of these in the Glance API log:

2011-09-28 17:46:12 DEBUG [glance.store.filesystem] Found image at /opt/stack/glance/images/3. Returning in ChunkedFile.
2011-09-28 17:46:12 DEBUG [eventlet.wsgi.server] Traceback (most recent call last):
    File "/usr/lib/pymodules/python2.7/eventlet/wsgi.py", line 351, in handle_one_response
        write(''.join(towrite))
      File "/usr/lib/pymodules/python2.7/eventlet/wsgi.py", line 301, in write
        _writelines(towrite)
      File "/usr/lib/python2.7/socket.py", line 334, in writelines
        self.flush()
      File "/usr/lib/python2.7/socket.py", line 303, in flush
        self._sock.sendall(view[write_offset:write_offset+buffer_size])
      File "/usr/lib/pymodules/python2.7/eventlet/greenio.py", line 283, in sendall
        tail = self.send(data, flags)
      File "/usr/lib/pymodules/python2.7/eventlet/greenio.py", line 269, in send
        total_sent += fd.send(data[total_sent:], flags)
    error: [Errno 104] Connection reset by peer

This patch fixes the improper calls to get_image() by replacing them
with appropriate calls to get_image_meta().

Change-Id: I741a207ba0e222820492aeb48bab9464d17539ab
This commit is contained in:
Jay Pipes 2011-12-06 12:27:19 -05:00
parent 6434611696
commit 068c782d03
8 changed files with 38 additions and 26 deletions

View File

@ -68,7 +68,19 @@ def image_delete(request, image_id):
def image_get(request, image_id):
return Image(glance_api(request).get_image(image_id)[0])
"""
Returns the actual image file from Glance for image with
supplied identifier
"""
return glance_api(request).get_image(image_id)[1]
def image_get_meta(request, image_id):
"""
Returns an Image object populated with metadata for image
with supplied identifier.
"""
return Image(glance_api(request).get_image_meta(image_id))
def image_list_detailed(request):

View File

@ -89,7 +89,7 @@ class Server(APIResourceWrapper):
from glance.common import exception as glance_exceptions
from horizon.api import glance
try:
image = glance.image_get(self.request, self.image['id'])
image = glance.image_get_meta(self.request, self.image['id'])
return image.name
except glance_exceptions.NotFound:
return "(not found)"

View File

@ -57,7 +57,7 @@ class UpdateImageForm(forms.SelfHandlingForm):
error_updating = _('Error updating image with id: %s' % image_id)
try:
image = api.image_get(request, image_id)
image = api.image_get_meta(request, image_id)
except glance_exception.ClientConnectionError, e:
LOG.exception(_('Error connecting to glance'))
messages.error(request, error_retrieving)
@ -152,7 +152,7 @@ class LaunchForm(forms.SelfHandlingForm):
image_id = data['image_id']
tenant_id = data['tenant_id']
try:
image = api.image_get(request, image_id)
image = api.image_get_meta(request, image_id)
flavor = api.flavor_get(request, data['flavor'])
api.server_create(request,
data['name'],
@ -181,7 +181,7 @@ class DeleteImage(forms.SelfHandlingForm):
image_id = data['image_id']
tenant_id = request.user.tenant_id
try:
image = api.image_get(request, image_id)
image = api.image_get_meta(request, image_id)
if image.owner == request.user.username:
api.image_delete(request, image_id)
else:

View File

@ -153,8 +153,8 @@ class ImageViewTests(test.BaseViewTests):
def test_launch_get(self):
IMAGE_ID = '1'
self.mox.StubOutWithMock(api, 'image_get')
api.image_get(IsA(http.HttpRequest),
self.mox.StubOutWithMock(api, 'image_get_meta')
api.image_get_meta(IsA(http.HttpRequest),
IMAGE_ID).AndReturn(self.visibleImage)
self.mox.StubOutWithMock(api, 'tenant_quota_get')
@ -207,8 +207,8 @@ class ImageViewTests(test.BaseViewTests):
'security_groups': 'default',
}
self.mox.StubOutWithMock(api, 'image_get')
api.image_get(IsA(http.HttpRequest),
self.mox.StubOutWithMock(api, 'image_get_meta')
api.image_get_meta(IsA(http.HttpRequest),
IMAGE_ID).AndReturn(self.visibleImage)
self.mox.StubOutWithMock(api, 'tenant_quota_get')
@ -226,7 +226,7 @@ class ImageViewTests(test.BaseViewTests):
self.security_groups)
# called again by the form
api.image_get(IsA(http.HttpRequest),
api.image_get_meta(IsA(http.HttpRequest),
IMAGE_ID).AndReturn(self.visibleImage)
self.mox.StubOutWithMock(api, 'flavor_get')
@ -254,8 +254,8 @@ class ImageViewTests(test.BaseViewTests):
def test_launch_flavorlist_error(self):
IMAGE_ID = '1'
self.mox.StubOutWithMock(api, 'image_get')
api.image_get(IsA(http.HttpRequest),
self.mox.StubOutWithMock(api, 'image_get_meta')
api.image_get_meta(IsA(http.HttpRequest),
IMAGE_ID).AndReturn(self.visibleImage)
self.mox.StubOutWithMock(api, 'tenant_quota_get')
@ -288,8 +288,8 @@ class ImageViewTests(test.BaseViewTests):
def test_launch_keypairlist_error(self):
IMAGE_ID = '2'
self.mox.StubOutWithMock(api, 'image_get')
api.image_get(IsA(http.HttpRequest),
self.mox.StubOutWithMock(api, 'image_get_meta')
api.image_get_meta(IsA(http.HttpRequest),
IMAGE_ID).AndReturn(self.visibleImage)
self.mox.StubOutWithMock(api, 'tenant_quota_get')
@ -336,8 +336,8 @@ class ImageViewTests(test.BaseViewTests):
'security_groups': 'default',
}
self.mox.StubOutWithMock(api, 'image_get')
api.image_get(IgnoreArg(),
self.mox.StubOutWithMock(api, 'image_get_meta')
api.image_get_meta(IgnoreArg(),
IMAGE_ID).AndReturn(self.visibleImage)
self.mox.StubOutWithMock(api, 'tenant_quota_get')
@ -355,7 +355,7 @@ class ImageViewTests(test.BaseViewTests):
self.security_groups)
# called again by the form
api.image_get(IgnoreArg(),
api.image_get_meta(IgnoreArg(),
IMAGE_ID).AndReturn(self.visibleImage)
self.mox.StubOutWithMock(api, 'flavor_get')

View File

@ -110,7 +110,7 @@ def launch(request, image_id):
tenant_id = request.user.tenant_id
# TODO(mgius): Any reason why these can't be after the launchform logic?
# If The form is valid, we've just wasted these two api calls
image = api.image_get(request, image_id)
image = api.image_get_meta(request, image_id)
quotas = api.tenant_quota_get(request, request.user.tenant_id)
try:
quotas.ram = int(quotas.ram)
@ -139,7 +139,7 @@ def launch(request, image_id):
@login_required
def update(request, image_id):
try:
image = api.image_get(request, image_id)
image = api.image_get_meta(request, image_id)
except glance_exception.ClientConnectionError, e:
LOG.exception("Error connecting to glance")
messages.error(request, _("Error connecting to glance: %s")

View File

@ -70,7 +70,7 @@ def index(request):
@login_required
def update(request, image_id):
try:
image = api.image_get(request, image_id)
image = api.image_get_meta(request, image_id)
except glance_exception.ClientConnectionError, e:
LOG.exception("Error connecting to glance")
messages.error(request,

View File

@ -79,18 +79,18 @@ class GlanceApiTests(APITestCase):
self.assertEqual(ret_val, TEST_RETURN)
def test_image_get(self):
def test_image_get_meta(self):
IMAGE_ID = '1'
glance_api = self.stub_glance_api()
glance_api.get_image(IMAGE_ID).AndReturn([TEST_RETURN])
glance_api.get_image_meta(IMAGE_ID).AndReturn([TEST_RETURN])
self.mox.ReplayAll()
ret_val = api.image_get(self.request, IMAGE_ID)
ret_val = api.image_get_meta(self.request, IMAGE_ID)
self.assertIsInstance(ret_val, api.Image)
self.assertEqual(ret_val._apidict, TEST_RETURN)
self.assertEqual(ret_val._apidict, [TEST_RETURN])
def test_image_list_detailed(self):
images = (TEST_RETURN, TEST_RETURN + '2')

View File

@ -99,8 +99,8 @@ class ServerWrapperTests(test.TestCase):
def test_image_name(self):
image = api.Image({'name': self.IMAGE_NAME})
self.mox.StubOutWithMock(api.glance, 'image_get')
api.glance.image_get(IsA(http.HttpRequest),
self.mox.StubOutWithMock(api.glance, 'image_get_meta')
api.glance.image_get_meta(IsA(http.HttpRequest),
self.IMAGE_OBJ['id']).AndReturn(image)
server = api.Server(self.inner_server, self.request)