Wrap image data in iterator

This is establishing the API for a future optimization. We want to
be able to offer true socket-level caching, but can't do that with
httplib2 right now. For now, we will just fake the optimization
by returning an iterator over the image body, which happens to already
be fully loaded into a string.

Change-Id: I2d36e3cdd45b26d7c7c27ba050bf6a4b5765df6c
This commit is contained in:
Brian Waldon
2012-07-11 19:34:28 -07:00
parent a814c15465
commit da360462a5
3 changed files with 34 additions and 6 deletions

View File

@@ -5,6 +5,7 @@ OpenStack Client interface. Handles the REST calls and responses.
import copy
import logging
import os
import StringIO
import urlparse
import httplib2
@@ -25,6 +26,7 @@ from glanceclient.common import exceptions
logger = logging.getLogger(__name__)
USER_AGENT = 'python-glanceclient'
CHUNKSIZE = 1024 * 64 # 64kB
class HTTPClient(httplib2.Http):
@@ -88,7 +90,12 @@ class HTTPClient(httplib2.Http):
# Redirected. Reissue the request to the new location.
return self._http_request(resp['location'], method, **kwargs)
return resp, body
#NOTE(bcwaldon): body has been loaded to a string at this point,
# but we want to move to a world where it can be read from a
# socket-level cache. This is here until we can do that.
body_iter = ResponseBodyIterator(StringIO.StringIO(body))
return resp, body_iter
def json_request(self, method, url, **kwargs):
kwargs.setdefault('headers', {})
@@ -97,7 +104,8 @@ class HTTPClient(httplib2.Http):
if 'body' in kwargs:
kwargs['body'] = json.dumps(kwargs['body'])
resp, body = self._http_request(url, method, **kwargs)
resp, body_iter = self._http_request(url, method, **kwargs)
body = ''.join([chunk for chunk in body_iter])
if body:
try:
@@ -115,3 +123,21 @@ class HTTPClient(httplib2.Http):
kwargs['headers'].setdefault('Content-Type',
'application/octet-stream')
return self._http_request(url, method, **kwargs)
class ResponseBodyIterator(object):
"""A class that acts as an iterator over an HTTP response."""
def __init__(self, resp):
self.resp = resp
def __iter__(self):
while True:
yield self.next()
def next(self):
chunk = self.resp.read(CHUNKSIZE)
if chunk:
return chunk
else:
raise StopIteration()

View File

@@ -159,8 +159,9 @@ class ImageManager(base.Manager):
if copy_from is not None:
hdrs['x-glance-api-copy-from'] = copy_from
resp, body = self.api.raw_request(
resp, body_iter = self.api.raw_request(
'POST', '/v1/images', headers=hdrs, body=image_data)
body = ''.join([c for c in body_iter])
return Image(self, json.loads(body)['image'])
def update(self, image, **kwargs):
@@ -199,6 +200,7 @@ class ImageManager(base.Manager):
hdrs['x-glance-api-copy-from'] = copy_from
url = '/v1/images/%s' % base.getid(image)
resp, body = self.api.raw_request(
resp, body_iter = self.api.raw_request(
'PUT', url, headers=hdrs, body=image_data)
body = ''.join([c for c in body_iter])
return Image(self, json.loads(body)['image'])

View File

@@ -131,7 +131,7 @@ class ImageManagerTest(unittest.TestCase):
self.assertEqual(image.name, 'image-1')
def test_data(self):
data = self.mgr.data('1')
data = ''.join([b for b in self.mgr.data('1')])
expect = [('GET', '/v1/images/1', {}, None)]
self.assertEqual(self.api.calls, expect)
self.assertEqual(data, 'XXX')
@@ -256,7 +256,7 @@ class ImageTest(unittest.TestCase):
def test_data(self):
image = self.mgr.get('1')
data = image.data()
data = ''.join([b for b in image.data()])
expect = [
('HEAD', '/v1/images/1', {}, None),
('GET', '/v1/images/1', {}, None),