From c72e4dd2b581366c85babbda8b0a5aa23de70363 Mon Sep 17 00:00:00 2001 From: Brian Waldon Date: Tue, 3 Apr 2012 17:39:32 -0700 Subject: [PATCH] image membership management works --- glanceclient/common/http.py | 13 +++++-- glanceclient/v1/image_members.py | 6 ++-- glanceclient/v1/images.py | 5 +-- glanceclient/v1/shell.py | 61 +++++++++++++++++++++++++------- tests/v1/test_image_members.py | 10 ++---- tests/v1/utils.py | 10 +++--- 6 files changed, 72 insertions(+), 33 deletions(-) diff --git a/glanceclient/common/http.py b/glanceclient/common/http.py index 4d1d7eb2..9ac51f21 100644 --- a/glanceclient/common/http.py +++ b/glanceclient/common/http.py @@ -57,6 +57,8 @@ class HTTPClient(httplib2.Http): string_parts.append(header) logger.debug("REQ: %s\n" % "".join(string_parts)) + if 'raw_body' in kwargs: + logger.debug("REQ BODY (RAW): %s\n" % (kwargs['raw_body'])) if 'body' in kwargs: logger.debug("REQ BODY: %s\n" % (kwargs['body'])) logger.debug("RESP: %s\nRESP BODY: %s\n", resp, body) @@ -71,9 +73,14 @@ class HTTPClient(httplib2.Http): _kwargs = copy.copy(kwargs) _kwargs.setdefault('headers', kwargs.get('headers', {})) _kwargs['headers']['User-Agent'] = USER_AGENT - if 'body' in kwargs and kwargs['body'] is not None: - _kwargs['headers']['Content-Type'] = 'application/octet-stream' - _kwargs['body'] = kwargs['body'] + if 'raw_body' in _kwargs: + raw_body = _kwargs.pop('raw_body') + if raw_body is not None: + _kwargs['headers']['Content-Type'] = 'application/octet-stream' + _kwargs['body'] = raw_body + elif 'body' in kwargs and kwargs['body'] is not None: + _kwargs['headers']['Content-Type'] = 'application/json' + _kwargs['body'] = json.dumps(kwargs['body']) resp, body = super(HTTPClient, self).request(url, method, **_kwargs) self.http_log((url, method,), _kwargs, resp, body) diff --git a/glanceclient/v1/image_members.py b/glanceclient/v1/image_members.py index e06b42dc..f1863f9d 100644 --- a/glanceclient/v1/image_members.py +++ b/glanceclient/v1/image_members.py @@ -64,16 +64,14 @@ class ImageMemberManager(base.Manager): def _list_by_member(self, member): member_id = base.getid(member) - resp, body = self.api.get('/v1/shared_images/%s' % member_id) + resp, body = self.api.get('/v1/shared-images/%s' % member_id) out = [] for member in body['shared_images']: member['member_id'] = member_id out.append(ImageMember(self, member, loaded=True)) return out - def delete(self, member): - member_id = member.member_id - image_id = member.image_id + def delete(self, image_id, member_id): self._delete("/v1/images/%s/members/%s" % (image_id, member_id)) def create(self, image, member_id, can_share=False): diff --git a/glanceclient/v1/images.py b/glanceclient/v1/images.py index c2800ca1..8f47f32f 100644 --- a/glanceclient/v1/images.py +++ b/glanceclient/v1/images.py @@ -140,7 +140,8 @@ class ImageManager(base.Manager): if copy_from is not None: hdrs['x-glance-api-copy-from'] = copy_from - resp, body = self.api.post('/v1/images', headers=hdrs, body=image_data) + resp, body = self.api.post('/v1/images', headers=hdrs, + raw_body=image_data) return Image(self, body['image']) def update(self, image, **kwargs): @@ -171,5 +172,5 @@ class ImageManager(base.Manager): image_id = base.getid(image) resp, body = self.api.put('/v1/images/%s' % image_id, headers=hdrs, - body=image_data) + raw_body=image_data) return Image(self, body['image']) diff --git a/glanceclient/v1/shell.py b/glanceclient/v1/shell.py index c79f83ed..0a146419 100644 --- a/glanceclient/v1/shell.py +++ b/glanceclient/v1/shell.py @@ -47,18 +47,18 @@ def do_image_show(gc, args): help='ID of image to reserve.') @utils.arg('--name', metavar='', help='Name of image.') -@utils.arg('--disk_format', metavar='', +@utils.arg('--disk-format', metavar='', help='Disk format of image.') -@utils.arg('--container_format', metavar='', +@utils.arg('--container-format', metavar='', help='Container format of image.') @utils.arg('--owner', metavar='', help='Tenant who should own image.') @utils.arg('--size', metavar='', help=('Size of image data (in bytes). Only used with' ' \'--location\' and \'--copy_from\'.')) -@utils.arg('--min_disk', metavar='', +@utils.arg('--min-disk', metavar='', help='Minimum size of disk needed to boot image (in gigabytes).') -@utils.arg('--min_ram', metavar='', +@utils.arg('--min-ram', metavar='', help='Minimum amount of ram needed to boot image (in megabytes).') @utils.arg('--location', metavar='', help=('URL where the data for this image already resides.' @@ -67,7 +67,7 @@ def do_image_show(gc, args): ' you would specify \'file:///usr/share/image.tar.gz\'.')) @utils.arg('--checksum', metavar='', help='Hash of image data used Glance can use for verification.') -@utils.arg('--copy_from', metavar='', +@utils.arg('--copy-from', metavar='', help=('Similar to \'--location\' in usage, but this indicates that' ' the Glance server should immediately copy the data and' ' store it in its configured image store.')) @@ -104,17 +104,17 @@ def do_image_create(gc, args): @utils.arg('id', metavar='', help='ID of image to modify.') @utils.arg('--name', metavar='', help='Name of image.') -@utils.arg('--disk_format', metavar='', +@utils.arg('--disk-format', metavar='', help='Disk format of image.') -@utils.arg('--container_format', metavar='', +@utils.arg('--container-format', metavar='', help='Container format of image.') @utils.arg('--owner', metavar='', help='Tenant who should own image.') @utils.arg('--size', metavar='', help='Size of image data (in bytes).') -@utils.arg('--min_disk', metavar='', +@utils.arg('--min-disk', metavar='', help='Minimum size of disk needed to boot image (in gigabytes).') -@utils.arg('--min_ram', metavar='', +@utils.arg('--min-ram', metavar='', help='Minimum amount of ram needed to boot image (in megabytes).') @utils.arg('--location', metavar='', help=('URL where the data for this image already resides.' @@ -123,13 +123,13 @@ def do_image_create(gc, args): ' you would specify \'file:///usr/share/image.tar.gz\'.')) @utils.arg('--checksum', metavar='', help='Hash of image data used Glance can use for verification.') -@utils.arg('--copy_from', metavar='', +@utils.arg('--copy-from', metavar='', help=('Similar to \'--location\' in usage, but this indicates that' ' the Glance server should immediately copy the data and' ' store it in its configured image store.')) -@utils.arg('--is_public', type=bool, +@utils.arg('--is-public', type=bool, help='Make image accessible to the public.') -@utils.arg('--is_protected', type=bool, +@utils.arg('--is-protected', type=bool, help='Prevent image from being deleted.') @utils.arg('--property', metavar="", action='append', default=[], help=("Arbitrary property to associate with image. " @@ -161,3 +161,40 @@ def do_image_update(gc, args): def do_image_delete(gc, args): """Delete a specific image.""" gc.images.delete(args.id) + + +@utils.arg('--image-id', metavar='', + help='Filter results by an image ID.') +@utils.arg('--tenant-id', metavar='', + help='Filter results by a tenant ID.') +def do_member_list(gc, args): + if args.image_id and args.tenant_id: + print 'Unable to filter members by both --image-id and --tenant-id.' + sys.exit(1) + elif args.image_id: + kwargs = {'image': args.image_id} + elif args.tenant_id: + kwargs = {'member': args.tenant_id} + else: + print 'Unable to list all members. Specify --image-id or --tenant-id' + sys.exit(1) + + members = gc.image_members.list(**kwargs) + columns = ['Image ID', 'Member ID', 'Can Share'] + utils.print_list(members, columns) + +@utils.arg('image_id', metavar='', + help='Image to add member to.') +@utils.arg('tenant_id', metavar='', + help='Tenant to add as member') +@utils.arg('--can-share', action='store_true', default=False, + help='Allow the specified tenant to share this image.') +def do_member_create(gc, args): + gc.image_members.create(args.image_id, args.tenant_id, args.can_share) + +@utils.arg('image_id', metavar='', + help='Image to add member to.') +@utils.arg('tenant_id', metavar='', + help='Tenant to add as member') +def do_member_delete(gc, args): + gc.image_members.delete(args.image_id, args.tenant_id) diff --git a/tests/v1/test_image_members.py b/tests/v1/test_image_members.py index 9fa94154..4613a505 100644 --- a/tests/v1/test_image_members.py +++ b/tests/v1/test_image_members.py @@ -25,7 +25,7 @@ class ImageMemberManagerTest(unittest.TestCase): def test_list_by_member(self): members = self.mgr.list(member='1') - expect = [('GET', '/v1/shared_images/1', {}, None)] + expect = [('GET', '/v1/shared-images/1', {}, None)] self.assertEqual(self.api.calls, expect) self.assertEqual(len(members), 1) self.assertEqual(members[0].member_id, '1') @@ -41,12 +41,8 @@ class ImageMemberManagerTest(unittest.TestCase): self.assertEqual(member.can_share, False) def test_delete(self): - member = self.mgr.get(self.image, '1') - self.mgr.delete(member) - expect = [ - ('GET', '/v1/images/1/members/1', {}, None), - ('DELETE', '/v1/images/1/members/1', {}, None), - ] + self.mgr.delete('1', '1') + expect = [('DELETE', '/v1/images/1/members/1', {}, None)] self.assertEqual(self.api.calls, expect) def test_create(self): diff --git a/tests/v1/utils.py b/tests/v1/utils.py index d67df3a1..4a3bb41e 100644 --- a/tests/v1/utils.py +++ b/tests/v1/utils.py @@ -87,7 +87,7 @@ fixtures = { 'PUT': ({}, None), 'DELETE': ({}, None), }, - '/v1/shared_images/1': { + '/v1/shared-images/1': { 'GET': ( {}, {'shared_images': [ @@ -116,11 +116,11 @@ class FakeAPI(object): def head(self, url): return self._request('HEAD', url) - def post(self, url, headers=None, body=None): - return self._request('POST', url, headers, body) + def post(self, url, headers=None, body=None, raw_body=None): + return self._request('POST', url, headers, body or raw_body) - def put(self, url, headers=None, body=None): - return self._request('PUT', url, headers, body) + def put(self, url, headers=None, body=None, raw_body=None): + return self._request('PUT', url, headers, body or raw_body) def delete(self, url): return self._request('DELETE', url)