Skip to content

Commit

Permalink
Fixes LP Bug#862664 - Improper calls to get_image
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jaypipes committed Dec 6, 2011
1 parent 6434611 commit 068c782
Show file tree
Hide file tree
Showing 8 changed files with 38 additions and 26 deletions.
14 changes: 13 additions & 1 deletion horizon/horizon/api/glance.py
Expand Up @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion horizon/horizon/api/nova.py
Expand Up @@ -89,7 +89,7 @@ def image_name(self):
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)"
Expand Down
6 changes: 3 additions & 3 deletions horizon/horizon/dashboards/nova/images/forms.py
Expand Up @@ -57,7 +57,7 @@ def handle(self, request, data):
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)
Expand Down Expand Up @@ -152,7 +152,7 @@ def handle(self, request, data):
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'],
Expand Down Expand Up @@ -181,7 +181,7 @@ def handle(self, request, data):
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:
Expand Down
24 changes: 12 additions & 12 deletions horizon/horizon/dashboards/nova/images/tests.py
Expand Up @@ -153,8 +153,8 @@ def test_index_glance_error(self):
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')
Expand Down Expand Up @@ -207,8 +207,8 @@ def test_launch_post(self):
'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')
Expand All @@ -226,7 +226,7 @@ def test_launch_post(self):
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')
Expand Down Expand Up @@ -254,8 +254,8 @@ def test_launch_post(self):
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')
Expand Down Expand Up @@ -288,8 +288,8 @@ def test_launch_flavorlist_error(self):
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')
Expand Down Expand Up @@ -336,8 +336,8 @@ def test_launch_form_apiexception(self):
'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')
Expand All @@ -355,7 +355,7 @@ def test_launch_form_apiexception(self):
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')
Expand Down
4 changes: 2 additions & 2 deletions horizon/horizon/dashboards/nova/images/views.py
Expand Up @@ -110,7 +110,7 @@ def securitygrouplist():
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)
Expand Down Expand Up @@ -139,7 +139,7 @@ def securitygrouplist():
@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")
Expand Down
2 changes: 1 addition & 1 deletion horizon/horizon/dashboards/syspanel/images/views.py
Expand Up @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions horizon/horizon/tests/api_tests/glance.py
Expand Up @@ -79,18 +79,18 @@ def test_image_delete(self):

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')
Expand Down
4 changes: 2 additions & 2 deletions horizon/horizon/tests/api_tests/nova.py
Expand Up @@ -99,8 +99,8 @@ def test_get_other_missing(self):

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)
Expand Down

0 comments on commit 068c782

Please sign in to comment.