Merge "Add v1 client side owner based filtering"

This commit is contained in:
Jenkins
2013-07-30 17:31:06 +00:00
committed by Gerrit Code Review
3 changed files with 157 additions and 6 deletions
+27 -6
View File
@@ -142,24 +142,42 @@ class ImageManager(base.Manager):
list than that represented by this image id
:param filters: dict of direct comparison filters that mimics the
structure of an image object
:param owner: If provided, only images with this owner (tenant id)
will be listed. An empty string ('') matches ownerless
images.
:rtype: list of :class:`Image`
"""
absolute_limit = kwargs.get('limit')
def paginate(qp, seen=0):
# Note(flaper87) Url encoding should
# be moved inside http utils, at least
# shouldn't be here.
#
# Making sure all params are str before
# trying to encode them
def filter_owner(owner, image):
# If client side owner 'filter' is specified
# only return images that match 'owner'.
if owner is None:
# Do not filter based on owner
return False
if (not hasattr(image, 'owner')) or image.owner is None:
# ownerless image
return not (owner == '')
else:
return not (image.owner == owner)
owner = qp.pop('owner', None)
for param, value in qp.iteritems():
if isinstance(value, basestring):
# Note(flaper87) Url encoding should
# be moved inside http utils, at least
# shouldn't be here.
#
# Making sure all params are str before
# trying to encode them
qp[param] = strutils.safe_encode(value)
url = '/v1/images/detail?%s' % urllib.urlencode(qp)
images = self._list(url, "images")
for image in images:
if filter_owner(owner, image):
continue
seen += 1
if absolute_limit is not None and seen > absolute_limit:
return
@@ -198,6 +216,9 @@ class ImageManager(base.Manager):
for key, value in properties.items():
params['property-%s' % key] = value
params.update(filters)
if kwargs.get('owner') is not None:
params['owner'] = kwargs['owner']
params['is_public'] = None
return paginate(params)
+8
View File
@@ -66,6 +66,13 @@ DISK_FORMATS = ('Acceptable formats: ami, ari, aki, vhd, vmdk, raw, '
@utils.arg('--is-public', type=utils.string_to_bool, metavar='{True|False}',
help=('Allows the user to select a listing of public or non '
'public images.'))
@utils.arg('--owner', default=None, metavar='<TENANT_ID>',
help='Display only images owned by this tenant id. Filtering '
'occurs on the client side so may be inefficient. This option '
'is mainly intended for admin use. Use an empty string (\'\') '
'to list images with no owner. Note: This option overrides '
'the --is-public argument if present. Note: the v2 API '
'supports more efficient server-side owner based filtering.')
def do_image_list(gc, args):
"""List images you can access."""
filter_keys = ['name', 'status', 'container_format', 'disk_format',
@@ -83,6 +90,7 @@ def do_image_list(gc, args):
kwargs['sort_key'] = args.sort_key
kwargs['sort_dir'] = args.sort_dir
kwargs['owner'] = args.owner
images = gc.images.list(**kwargs)
+122
View File
@@ -68,6 +68,90 @@ fixtures = {
]},
),
},
'/v1/images/detail?is_public=None&limit=20': {
'GET': (
{},
{'images': [
{
'id': 'a',
'owner': 'A',
'name': 'image-1',
'properties': {'arch': 'x86_64'},
},
{
'id': 'b',
'owner': 'B',
'name': 'image-2',
'properties': {'arch': 'x86_64'},
},
{
'id': 'c',
'name': 'image-3',
'properties': {'arch': 'x86_64'},
},
]},
),
},
'/v1/images/detail?is_public=None&limit=5': {
'GET': (
{},
{'images': [
{
'id': 'a',
'owner': 'A',
'name': 'image-1',
'properties': {'arch': 'x86_64'},
},
{
'id': 'b',
'owner': 'B',
'name': 'image-2',
'properties': {'arch': 'x86_64'},
},
{
'id': 'b2',
'owner': 'B',
'name': 'image-3',
'properties': {'arch': 'x86_64'},
},
{
'id': 'c',
'name': 'image-3',
'properties': {'arch': 'x86_64'},
},
]},
),
},
'/v1/images/detail?limit=5': {
'GET': (
{},
{'images': [
{
'id': 'a',
'owner': 'A',
'name': 'image-1',
'properties': {'arch': 'x86_64'},
},
{
'id': 'b',
'owner': 'B',
'name': 'image-2',
'properties': {'arch': 'x86_64'},
},
{
'id': 'b2',
'owner': 'B',
'name': 'image-3',
'properties': {'arch': 'x86_64'},
},
{
'id': 'c',
'name': 'image-3',
'properties': {'arch': 'x86_64'},
},
]},
),
},
'/v1/images/detail?marker=a&limit=%d' % images.DEFAULT_PAGE_SIZE: {
'GET': (
{},
@@ -501,6 +585,44 @@ class ImageManagerTest(testtools.TestCase):
headers = self.mgr._image_meta_from_headers(fields)
self.assertEqual(headers["name"], u"ni\xf1o")
def test_image_list_with_owner(self):
images = self.mgr.list(owner='A', page_size=20)
image_list = list(images)
self.assertEqual(image_list[0].owner, 'A')
self.assertEqual(image_list[0].id, 'a')
self.assertEqual(len(image_list), 1)
def test_image_list_with_notfound_owner(self):
images = self.mgr.list(owner='X', page_size=20)
self.assertEqual(len(list(images)), 0)
def test_image_list_with_empty_string_owner(self):
images = self.mgr.list(owner='', page_size=20)
image_list = list(images)
self.assertRaises(AttributeError, lambda: image_list[0].owner)
self.assertEqual(image_list[0].id, 'c')
self.assertEqual(len(image_list), 1)
def test_image_list_with_unspecified_owner(self):
images = self.mgr.list(owner=None, page_size=5)
image_list = list(images)
self.assertEqual(image_list[0].owner, 'A')
self.assertEqual(image_list[0].id, 'a')
self.assertEqual(image_list[1].owner, 'B')
self.assertEqual(image_list[1].id, 'b')
self.assertEqual(image_list[2].owner, 'B')
self.assertEqual(image_list[2].id, 'b2')
self.assertRaises(AttributeError, lambda: image_list[3].owner)
self.assertEqual(image_list[3].id, 'c')
self.assertEqual(len(image_list), 4)
def test_image_list_with_owner_and_limit(self):
images = self.mgr.list(owner='B', page_size=5, limit=1)
image_list = list(images)
self.assertEqual(image_list[0].owner, 'B')
self.assertEqual(image_list[0].id, 'b')
self.assertEqual(len(image_list), 1)
class ImageTest(testtools.TestCase):
def setUp(self):