Add support for image size in v2 api upload

Some backend stores e.g. RBD, will fail if told to create an
image without a valid size (RBD will fail to write to a zero-size
image). Here we add support to allow the image size to be provided
when doing an upload. The result is that the upload content-length
will be set if available either from checking the supplied file
object or as provided by user.

Closes-Bug: 1220197
Change-Id: Ia1f2ea5680a139750d931591949b3e0058148b4b
This commit is contained in:
Edward Hope-Morley
2013-09-03 12:27:54 +01:00
parent 238e9fffcc
commit 4a41358cea
7 changed files with 135 additions and 9 deletions
+27 -3
View File
@@ -280,12 +280,36 @@ class HTTPClient(object):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type',
'application/octet-stream')
if 'body' in kwargs:
if (hasattr(kwargs['body'], 'read')
and method.lower() in ('post', 'put')):
if 'content_length' in kwargs:
content_length = kwargs.pop('content_length')
else:
content_length = None
if (('body' in kwargs) and (hasattr(kwargs['body'], 'read') and
method.lower() in ('post', 'put'))):
# NOTE(dosaboy): only use chunked transfer if not setting a
# content length since setting it will implicitly disable
# chunking.
file_content_length = utils.get_file_size(kwargs['body'])
if content_length is None:
content_length = file_content_length
elif (file_content_length and
(content_length != file_content_length)):
errmsg = ("supplied content-length (%s) does not match "
"length of supplied data (%s)" %
(content_length, file_content_length))
raise AttributeError(errmsg)
if content_length is None:
# We use 'Transfer-Encoding: chunked' because
# body size may not always be known in advance.
kwargs['headers']['Transfer-Encoding'] = 'chunked'
else:
kwargs['headers']['Content-Length'] = str(content_length)
return self._http_request(url, method, **kwargs)
+4 -2
View File
@@ -97,18 +97,20 @@ class Controller(object):
body.set_checksum(checksum)
return body
def upload(self, image_id, image_data):
def upload(self, image_id, image_data, image_size=None):
"""
Upload the data for an image.
:param image_id: ID of the image to upload data for.
:param image_data: File-like object supplying the data to upload.
:param image_size: Total size in bytes of image to be uploaded.
"""
url = '/v2/images/%s/file' % image_id
hdrs = {'Content-Type': 'application/octet-stream'}
self.http_client.raw_request('PUT', url,
headers=hdrs,
body=image_data)
body=image_data,
content_length=image_size)
def delete(self, image_id):
"""Delete an image."""
+6 -1
View File
@@ -218,12 +218,17 @@ def do_image_download(gc, args):
help=('Local file that contains disk image to be uploaded'
' during creation. Alternatively, images can be passed'
' to the client via stdin.'))
@utils.arg('--size', metavar='<IMAGE_SIZE>', type=int,
help='Size in bytes of image to be uploaded. Default is to get '
'size from provided data object but this is supported in case '
'where size cannot be inferred.',
default=None)
@utils.arg('id', metavar='<IMAGE_ID>',
help='ID of image to upload data to.')
def do_image_upload(gc, args):
"""Upload data for a specific image."""
image_data = utils.get_data_file(args)
gc.images.upload(args.id, image_data)
gc.images.upload(args.id, image_data, args.size)
@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to delete.')