Adding support for passing image data through cli

This commit is contained in:
Brian Waldon
2012-04-03 17:01:48 -07:00
parent d191262413
commit b87b1b5086
4 changed files with 71 additions and 8 deletions

View File

@@ -71,9 +71,9 @@ class HTTPClient(httplib2.Http):
_kwargs = copy.copy(kwargs)
_kwargs.setdefault('headers', kwargs.get('headers', {}))
_kwargs['headers']['User-Agent'] = USER_AGENT
if 'body' in kwargs:
_kwargs['headers']['Content-Type'] = 'application/json'
_kwargs['body'] = json.dumps(kwargs['body'])
if 'body' in kwargs and kwargs['body'] is not None:
_kwargs['headers']['Content-Type'] = 'application/octet-stream'
_kwargs['body'] = kwargs['body']
resp, body = super(HTTPClient, self).request(url, method, **_kwargs)
self.http_log((url, method,), _kwargs, resp, body)

View File

@@ -14,6 +14,8 @@
# under the License.
import copy
import errno
import os
import urllib
from glanceclient.common import base
@@ -88,12 +90,42 @@ class ImageManager(base.Manager):
"""Delete an image."""
self._delete("/v1/images/%s" % base.getid(image))
def _get_file_size(self, obj):
"""Analyze file-like object and attempt to determine its size.
:param obj: file-like object, typically redirected from stdin.
:retval The file's size or None if it cannot be determined.
"""
# For large images, we need to supply the size of the
# image file. See LP Bugs #827660 and #845788.
if hasattr(obj, 'seek') and hasattr(obj, 'tell'):
try:
obj.seek(0, os.SEEK_END)
obj_size = obj.tell()
obj.seek(0)
return obj_size
except IOError, e:
if e.errno == errno.ESPIPE:
# Illegal seek. This means the user is trying
# to pipe image data to the client, e.g.
# echo testdata | bin/glance add blah..., or
# that stdin is empty
return None
else:
raise
def create(self, **kwargs):
"""Create an image
TODO(bcwaldon): document accepted params
"""
image_data = kwargs.pop('data', None)
if image_data is not None:
image_size = self._get_file_size(image_data)
if image_size != 0:
kwargs.setdefault('size', image_size)
else:
image_data = None
fields = {}
for field in kwargs:
@@ -112,7 +144,18 @@ class ImageManager(base.Manager):
return Image(self, body['image'])
def update(self, image, **kwargs):
"""Update an image"""
"""Update an image
TODO(bcwaldon): document accepted params
"""
image_data = kwargs.pop('data', None)
if image_data is not None:
image_size = self._get_file_size(image_data)
if image_size != 0:
kwargs.setdefault('size', image_size)
else:
image_data = None
fields = {}
for field in kwargs:
if field in UPDATE_PARAMS:
@@ -127,5 +170,6 @@ class ImageManager(base.Manager):
hdrs['x-glance-api-copy-from'] = copy_from
image_id = base.getid(image)
resp, body = self.api.put('/v1/images/%s' % image_id, headers=hdrs)
resp, body = self.api.put('/v1/images/%s' % image_id, headers=hdrs,
body=image_data)
return Image(self, body['image'])

View File

@@ -14,6 +14,7 @@
# under the License.
import copy
import sys
from glanceclient.common import utils
import glanceclient.v1.images
@@ -53,7 +54,8 @@ def do_image_show(gc, args):
@utils.arg('--owner', metavar='<TENANT_ID>',
help='Tenant who should own image.')
@utils.arg('--size', metavar='<SIZE>',
help='Size of image data (in bytes).')
help=('Size of image data (in bytes). Only used with'
' \'--location\' and \'--copy_from\'.'))
@utils.arg('--min_disk', metavar='<DISK_GB>',
help='Minimum size of disk needed to boot image (in gigabytes).')
@utils.arg('--min_ram', metavar='<DISK_RAM>',
@@ -92,10 +94,14 @@ def do_image_create(gc, args):
CREATE_PARAMS = glanceclient.v1.images.CREATE_PARAMS
fields = dict(filter(lambda x: x[0] in CREATE_PARAMS, fields.items()))
if 'location' not in fields and 'copy_from' not in fields:
fields['data'] = sys.stdin
image = gc.images.create(**fields)
_image_show(image)
@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to modify.')
@utils.arg('--name', metavar='<NAME>',
help='Name of image.')
@utils.arg('--disk_format', metavar='<CONTAINER_FORMAT>',
@@ -132,6 +138,8 @@ def do_image_update(gc, args):
# Filter out None values
fields = dict(filter(lambda x: x[1] is not None, vars(args).items()))
image_id = fields.pop('id')
raw_properties = fields.pop('property')
fields['properties'] = {}
for datum in raw_properties:
@@ -142,7 +150,10 @@ def do_image_update(gc, args):
UPDATE_PARAMS = glanceclient.v1.images.UPDATE_PARAMS
fields = dict(filter(lambda x: x[0] in UPDATE_PARAMS, fields.items()))
image = gc.images.create(**fields)
if 'location' not in fields and 'copy_from' not in fields:
fields['data'] = sys.stdin
image = gc.images.update(image_id, **fields)
_image_show(image)

View File

@@ -86,7 +86,8 @@ class ImageManagerTest(unittest.TestCase):
def test_create_with_data(self):
image_data = StringIO.StringIO('XXX')
self.mgr.create(data=image_data)
expect = [('POST', '/v1/images', {}, image_data)]
expect_headers = {'x-image-meta-size': '3'}
expect = [('POST', '/v1/images', expect_headers, image_data)]
self.assertEqual(self.api.calls, expect)
def test_update(self):
@@ -119,6 +120,13 @@ class ImageManagerTest(unittest.TestCase):
self.assertEqual(image.id, '1')
self.assertEqual(image.name, 'image-2')
def test_update_with_data(self):
image_data = StringIO.StringIO('XXX')
self.mgr.update('1', data=image_data)
expect_headers = {'x-image-meta-size': '3'}
expect = [('PUT', '/v1/images/1', expect_headers, image_data)]
self.assertEqual(self.api.calls, expect)
class ImageTest(unittest.TestCase):
def setUp(self):