image membership management works
This commit is contained in:
@@ -57,6 +57,8 @@ class HTTPClient(httplib2.Http):
|
|||||||
string_parts.append(header)
|
string_parts.append(header)
|
||||||
|
|
||||||
logger.debug("REQ: %s\n" % "".join(string_parts))
|
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:
|
if 'body' in kwargs:
|
||||||
logger.debug("REQ BODY: %s\n" % (kwargs['body']))
|
logger.debug("REQ BODY: %s\n" % (kwargs['body']))
|
||||||
logger.debug("RESP: %s\nRESP BODY: %s\n", resp, body)
|
logger.debug("RESP: %s\nRESP BODY: %s\n", resp, body)
|
||||||
@@ -71,9 +73,14 @@ class HTTPClient(httplib2.Http):
|
|||||||
_kwargs = copy.copy(kwargs)
|
_kwargs = copy.copy(kwargs)
|
||||||
_kwargs.setdefault('headers', kwargs.get('headers', {}))
|
_kwargs.setdefault('headers', kwargs.get('headers', {}))
|
||||||
_kwargs['headers']['User-Agent'] = USER_AGENT
|
_kwargs['headers']['User-Agent'] = USER_AGENT
|
||||||
if 'body' in kwargs and kwargs['body'] is not None:
|
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['headers']['Content-Type'] = 'application/octet-stream'
|
||||||
_kwargs['body'] = kwargs['body']
|
_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)
|
resp, body = super(HTTPClient, self).request(url, method, **_kwargs)
|
||||||
self.http_log((url, method,), _kwargs, resp, body)
|
self.http_log((url, method,), _kwargs, resp, body)
|
||||||
|
@@ -64,16 +64,14 @@ class ImageMemberManager(base.Manager):
|
|||||||
|
|
||||||
def _list_by_member(self, member):
|
def _list_by_member(self, member):
|
||||||
member_id = base.getid(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 = []
|
out = []
|
||||||
for member in body['shared_images']:
|
for member in body['shared_images']:
|
||||||
member['member_id'] = member_id
|
member['member_id'] = member_id
|
||||||
out.append(ImageMember(self, member, loaded=True))
|
out.append(ImageMember(self, member, loaded=True))
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def delete(self, member):
|
def delete(self, image_id, member_id):
|
||||||
member_id = member.member_id
|
|
||||||
image_id = member.image_id
|
|
||||||
self._delete("/v1/images/%s/members/%s" % (image_id, member_id))
|
self._delete("/v1/images/%s/members/%s" % (image_id, member_id))
|
||||||
|
|
||||||
def create(self, image, member_id, can_share=False):
|
def create(self, image, member_id, can_share=False):
|
||||||
|
@@ -140,7 +140,8 @@ class ImageManager(base.Manager):
|
|||||||
if copy_from is not None:
|
if copy_from is not None:
|
||||||
hdrs['x-glance-api-copy-from'] = copy_from
|
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'])
|
return Image(self, body['image'])
|
||||||
|
|
||||||
def update(self, image, **kwargs):
|
def update(self, image, **kwargs):
|
||||||
@@ -171,5 +172,5 @@ class ImageManager(base.Manager):
|
|||||||
|
|
||||||
image_id = base.getid(image)
|
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)
|
raw_body=image_data)
|
||||||
return Image(self, body['image'])
|
return Image(self, body['image'])
|
||||||
|
@@ -47,18 +47,18 @@ def do_image_show(gc, args):
|
|||||||
help='ID of image to reserve.')
|
help='ID of image to reserve.')
|
||||||
@utils.arg('--name', metavar='<NAME>',
|
@utils.arg('--name', metavar='<NAME>',
|
||||||
help='Name of image.')
|
help='Name of image.')
|
||||||
@utils.arg('--disk_format', metavar='<CONTAINER_FORMAT>',
|
@utils.arg('--disk-format', metavar='<CONTAINER_FORMAT>',
|
||||||
help='Disk format of image.')
|
help='Disk format of image.')
|
||||||
@utils.arg('--container_format', metavar='<DISK_FORMAT>',
|
@utils.arg('--container-format', metavar='<DISK_FORMAT>',
|
||||||
help='Container format of image.')
|
help='Container format of image.')
|
||||||
@utils.arg('--owner', metavar='<TENANT_ID>',
|
@utils.arg('--owner', metavar='<TENANT_ID>',
|
||||||
help='Tenant who should own image.')
|
help='Tenant who should own image.')
|
||||||
@utils.arg('--size', metavar='<SIZE>',
|
@utils.arg('--size', metavar='<SIZE>',
|
||||||
help=('Size of image data (in bytes). Only used with'
|
help=('Size of image data (in bytes). Only used with'
|
||||||
' \'--location\' and \'--copy_from\'.'))
|
' \'--location\' and \'--copy_from\'.'))
|
||||||
@utils.arg('--min_disk', metavar='<DISK_GB>',
|
@utils.arg('--min-disk', metavar='<DISK_GB>',
|
||||||
help='Minimum size of disk needed to boot image (in gigabytes).')
|
help='Minimum size of disk needed to boot image (in gigabytes).')
|
||||||
@utils.arg('--min_ram', metavar='<DISK_RAM>',
|
@utils.arg('--min-ram', metavar='<DISK_RAM>',
|
||||||
help='Minimum amount of ram needed to boot image (in megabytes).')
|
help='Minimum amount of ram needed to boot image (in megabytes).')
|
||||||
@utils.arg('--location', metavar='<IMAGE_URL>',
|
@utils.arg('--location', metavar='<IMAGE_URL>',
|
||||||
help=('URL where the data for this image already resides.'
|
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\'.'))
|
' you would specify \'file:///usr/share/image.tar.gz\'.'))
|
||||||
@utils.arg('--checksum', metavar='<CHECKSUM>',
|
@utils.arg('--checksum', metavar='<CHECKSUM>',
|
||||||
help='Hash of image data used Glance can use for verification.')
|
help='Hash of image data used Glance can use for verification.')
|
||||||
@utils.arg('--copy_from', metavar='<IMAGE_URL>',
|
@utils.arg('--copy-from', metavar='<IMAGE_URL>',
|
||||||
help=('Similar to \'--location\' in usage, but this indicates that'
|
help=('Similar to \'--location\' in usage, but this indicates that'
|
||||||
' the Glance server should immediately copy the data and'
|
' the Glance server should immediately copy the data and'
|
||||||
' store it in its configured image store.'))
|
' store it in its configured image store.'))
|
||||||
@@ -104,17 +104,17 @@ def do_image_create(gc, args):
|
|||||||
@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to modify.')
|
@utils.arg('id', metavar='<IMAGE_ID>', help='ID of image to modify.')
|
||||||
@utils.arg('--name', metavar='<NAME>',
|
@utils.arg('--name', metavar='<NAME>',
|
||||||
help='Name of image.')
|
help='Name of image.')
|
||||||
@utils.arg('--disk_format', metavar='<CONTAINER_FORMAT>',
|
@utils.arg('--disk-format', metavar='<CONTAINER_FORMAT>',
|
||||||
help='Disk format of image.')
|
help='Disk format of image.')
|
||||||
@utils.arg('--container_format', metavar='<DISK_FORMAT>',
|
@utils.arg('--container-format', metavar='<DISK_FORMAT>',
|
||||||
help='Container format of image.')
|
help='Container format of image.')
|
||||||
@utils.arg('--owner', metavar='<TENANT_ID>',
|
@utils.arg('--owner', metavar='<TENANT_ID>',
|
||||||
help='Tenant who should own image.')
|
help='Tenant who should own image.')
|
||||||
@utils.arg('--size', metavar='<SIZE>',
|
@utils.arg('--size', metavar='<SIZE>',
|
||||||
help='Size of image data (in bytes).')
|
help='Size of image data (in bytes).')
|
||||||
@utils.arg('--min_disk', metavar='<DISK_GB>',
|
@utils.arg('--min-disk', metavar='<DISK_GB>',
|
||||||
help='Minimum size of disk needed to boot image (in gigabytes).')
|
help='Minimum size of disk needed to boot image (in gigabytes).')
|
||||||
@utils.arg('--min_ram', metavar='<DISK_RAM>',
|
@utils.arg('--min-ram', metavar='<DISK_RAM>',
|
||||||
help='Minimum amount of ram needed to boot image (in megabytes).')
|
help='Minimum amount of ram needed to boot image (in megabytes).')
|
||||||
@utils.arg('--location', metavar='<IMAGE_URL>',
|
@utils.arg('--location', metavar='<IMAGE_URL>',
|
||||||
help=('URL where the data for this image already resides.'
|
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\'.'))
|
' you would specify \'file:///usr/share/image.tar.gz\'.'))
|
||||||
@utils.arg('--checksum', metavar='<CHECKSUM>',
|
@utils.arg('--checksum', metavar='<CHECKSUM>',
|
||||||
help='Hash of image data used Glance can use for verification.')
|
help='Hash of image data used Glance can use for verification.')
|
||||||
@utils.arg('--copy_from', metavar='<IMAGE_URL>',
|
@utils.arg('--copy-from', metavar='<IMAGE_URL>',
|
||||||
help=('Similar to \'--location\' in usage, but this indicates that'
|
help=('Similar to \'--location\' in usage, but this indicates that'
|
||||||
' the Glance server should immediately copy the data and'
|
' the Glance server should immediately copy the data and'
|
||||||
' store it in its configured image store.'))
|
' 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.')
|
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.')
|
help='Prevent image from being deleted.')
|
||||||
@utils.arg('--property', metavar="<key=value>", action='append', default=[],
|
@utils.arg('--property', metavar="<key=value>", action='append', default=[],
|
||||||
help=("Arbitrary property to associate with image. "
|
help=("Arbitrary property to associate with image. "
|
||||||
@@ -161,3 +161,40 @@ def do_image_update(gc, args):
|
|||||||
def do_image_delete(gc, args):
|
def do_image_delete(gc, args):
|
||||||
"""Delete a specific image."""
|
"""Delete a specific image."""
|
||||||
gc.images.delete(args.id)
|
gc.images.delete(args.id)
|
||||||
|
|
||||||
|
|
||||||
|
@utils.arg('--image-id', metavar='<IMAGE_ID>',
|
||||||
|
help='Filter results by an image ID.')
|
||||||
|
@utils.arg('--tenant-id', metavar='<TENANT_ID>',
|
||||||
|
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='<IMAGE_ID>',
|
||||||
|
help='Image to add member to.')
|
||||||
|
@utils.arg('tenant_id', metavar='<TENANT_ID>',
|
||||||
|
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='<IMAGE_ID>',
|
||||||
|
help='Image to add member to.')
|
||||||
|
@utils.arg('tenant_id', metavar='<TENANT_ID>',
|
||||||
|
help='Tenant to add as member')
|
||||||
|
def do_member_delete(gc, args):
|
||||||
|
gc.image_members.delete(args.image_id, args.tenant_id)
|
||||||
|
@@ -25,7 +25,7 @@ class ImageMemberManagerTest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_list_by_member(self):
|
def test_list_by_member(self):
|
||||||
members = self.mgr.list(member='1')
|
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(self.api.calls, expect)
|
||||||
self.assertEqual(len(members), 1)
|
self.assertEqual(len(members), 1)
|
||||||
self.assertEqual(members[0].member_id, '1')
|
self.assertEqual(members[0].member_id, '1')
|
||||||
@@ -41,12 +41,8 @@ class ImageMemberManagerTest(unittest.TestCase):
|
|||||||
self.assertEqual(member.can_share, False)
|
self.assertEqual(member.can_share, False)
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
member = self.mgr.get(self.image, '1')
|
self.mgr.delete('1', '1')
|
||||||
self.mgr.delete(member)
|
expect = [('DELETE', '/v1/images/1/members/1', {}, None)]
|
||||||
expect = [
|
|
||||||
('GET', '/v1/images/1/members/1', {}, None),
|
|
||||||
('DELETE', '/v1/images/1/members/1', {}, None),
|
|
||||||
]
|
|
||||||
self.assertEqual(self.api.calls, expect)
|
self.assertEqual(self.api.calls, expect)
|
||||||
|
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
|
@@ -87,7 +87,7 @@ fixtures = {
|
|||||||
'PUT': ({}, None),
|
'PUT': ({}, None),
|
||||||
'DELETE': ({}, None),
|
'DELETE': ({}, None),
|
||||||
},
|
},
|
||||||
'/v1/shared_images/1': {
|
'/v1/shared-images/1': {
|
||||||
'GET': (
|
'GET': (
|
||||||
{},
|
{},
|
||||||
{'shared_images': [
|
{'shared_images': [
|
||||||
@@ -116,11 +116,11 @@ class FakeAPI(object):
|
|||||||
def head(self, url):
|
def head(self, url):
|
||||||
return self._request('HEAD', url)
|
return self._request('HEAD', url)
|
||||||
|
|
||||||
def post(self, url, headers=None, body=None):
|
def post(self, url, headers=None, body=None, raw_body=None):
|
||||||
return self._request('POST', url, headers, body)
|
return self._request('POST', url, headers, body or raw_body)
|
||||||
|
|
||||||
def put(self, url, headers=None, body=None):
|
def put(self, url, headers=None, body=None, raw_body=None):
|
||||||
return self._request('PUT', url, headers, body)
|
return self._request('PUT', url, headers, body or raw_body)
|
||||||
|
|
||||||
def delete(self, url):
|
def delete(self, url):
|
||||||
return self._request('DELETE', url)
|
return self._request('DELETE', url)
|
||||||
|
Reference in New Issue
Block a user