adding custom property api filtering
This commit is contained in:
@@ -130,8 +130,8 @@ class Controller(wsgi.Controller):
|
||||
|
||||
"""
|
||||
filters = {}
|
||||
for param in SUPPORTED_FILTERS:
|
||||
if param in req.str_params:
|
||||
for param in req.str_params:
|
||||
if param in SUPPORTED_FILTERS or param.startswith('property-'):
|
||||
filters[param] = req.str_params.get(param)
|
||||
|
||||
return filters
|
||||
|
@@ -172,9 +172,28 @@ def image_get_filtered(context, filters):
|
||||
del filters['size_max']
|
||||
|
||||
for (k, v) in filters.items():
|
||||
query = query.filter(getattr(models.Image, k) == v)
|
||||
if not k.startswith('property-'):
|
||||
query = query.filter(getattr(models.Image, k) == v)
|
||||
|
||||
return query.all()
|
||||
images = query.all()
|
||||
|
||||
#TODO(bcwaldon): use an actual sqlalchemy query to accomplish this
|
||||
def prop_filter(key, value):
|
||||
def func(image):
|
||||
for prop in image.properties:
|
||||
if prop.deleted == False and \
|
||||
prop.name == key and \
|
||||
prop.value == value:
|
||||
return True
|
||||
return False
|
||||
return func
|
||||
|
||||
for (k, v) in filters.items():
|
||||
if k.startswith('property-'):
|
||||
_k = k[9:]
|
||||
images = filter(prop_filter(_k, v), images)
|
||||
|
||||
return images
|
||||
|
||||
|
||||
def _drop_protected_attrs(model_class, values):
|
||||
|
@@ -102,8 +102,8 @@ class Controller(wsgi.Controller):
|
||||
|
||||
"""
|
||||
filters = {}
|
||||
for param in SUPPORTED_FILTERS:
|
||||
if param in req.str_params:
|
||||
for param in req.str_params:
|
||||
if param in SUPPORTED_FILTERS or param.startswith('property-'):
|
||||
filters[param] = req.str_params.get(param)
|
||||
|
||||
return filters
|
||||
|
@@ -816,6 +816,7 @@ class TestCurlApi(functional.FunctionalTest):
|
||||
"-H 'X-Image-Meta-Disk-Format: vdi' "
|
||||
"-H 'X-Image-Meta-Size: 19' "
|
||||
"-H 'X-Image-Meta-Is-Public: True' "
|
||||
"-H 'X-Image-Meta-Property-pants: are on' "
|
||||
"http://0.0.0.0:%d/v1/images") % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
@@ -834,6 +835,7 @@ class TestCurlApi(functional.FunctionalTest):
|
||||
"-H 'X-Image-Meta-Disk-Format: vhd' "
|
||||
"-H 'X-Image-Meta-Size: 20' "
|
||||
"-H 'X-Image-Meta-Is-Public: True' "
|
||||
"-H 'X-Image-Meta-Property-pants: are on' "
|
||||
"http://0.0.0.0:%d/v1/images") % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
@@ -851,6 +853,7 @@ class TestCurlApi(functional.FunctionalTest):
|
||||
"-H 'X-Image-Meta-Disk-Format: ami' "
|
||||
"-H 'X-Image-Meta-Size: 21' "
|
||||
"-H 'X-Image-Meta-Is-Public: True' "
|
||||
"-H 'X-Image-Meta-Property-pants: are off' "
|
||||
"http://0.0.0.0:%d/v1/images") % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
@@ -964,3 +967,17 @@ class TestCurlApi(functional.FunctionalTest):
|
||||
self.assertEqual(len(images["images"]), 2)
|
||||
for image in images["images"]:
|
||||
self.assertTrue(image["size"] >= 20)
|
||||
|
||||
# 9. GET /images with property filter
|
||||
# Verify correct images returned with property
|
||||
cmd = ("curl http://0.0.0.0:%d/v1/images/detail?"
|
||||
"property-pants=are%%20on" % api_port)
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
images = json.loads(out.strip())
|
||||
|
||||
self.assertEqual(len(images["images"]), 2)
|
||||
for image in images["images"]:
|
||||
self.assertEqual(image["properties"]["pants"], "are on")
|
||||
|
@@ -401,8 +401,20 @@ def stub_out_registry_db_image_api(stubs):
|
||||
size_max = int(filters.pop('size_max'))
|
||||
images = [f for f in images if int(f['size']) <= size_max]
|
||||
|
||||
def _prop_filter(key, value):
|
||||
def _func(image):
|
||||
for prop in image['properties']:
|
||||
if prop['name'] == key:
|
||||
return prop['value'] == value
|
||||
return False
|
||||
return _func
|
||||
|
||||
for k, v in filters.items():
|
||||
images = [f for f in images if f[k] == v]
|
||||
if k.startswith('property-'):
|
||||
_k = k[9:]
|
||||
images = filter(_prop_filter(_k, v), images)
|
||||
else:
|
||||
images = [f for f in images if f[k] == v]
|
||||
|
||||
return images
|
||||
|
||||
|
@@ -162,11 +162,6 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
public images that have a specific name
|
||||
|
||||
"""
|
||||
fixture = {'id': 2,
|
||||
'name': 'fake image #2',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
extra_fixture = {'id': 3,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
@@ -205,11 +200,6 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
public images that have a specific status
|
||||
|
||||
"""
|
||||
fixture = {'id': 2,
|
||||
'name': 'fake image #2',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
extra_fixture = {'id': 3,
|
||||
'status': 'saving',
|
||||
'is_public': True,
|
||||
@@ -248,11 +238,6 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
public images that have a specific container_format
|
||||
|
||||
"""
|
||||
fixture = {'id': 2,
|
||||
'name': 'fake image #2',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
extra_fixture = {'id': 3,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
@@ -291,11 +276,6 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
public images that have a specific disk_format
|
||||
|
||||
"""
|
||||
fixture = {'id': 2,
|
||||
'name': 'fake image #2',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
extra_fixture = {'id': 3,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
@@ -334,11 +314,6 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
public images that have a size greater than or equal to size_min
|
||||
|
||||
"""
|
||||
fixture = {'id': 2,
|
||||
'name': 'fake image #2',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
extra_fixture = {'id': 3,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
@@ -377,11 +352,6 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
public images that have a size less than or equal to size_max
|
||||
|
||||
"""
|
||||
fixture = {'id': 2,
|
||||
'name': 'fake image #2',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
extra_fixture = {'id': 3,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
@@ -421,11 +391,6 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
and greater than or equal to size_min
|
||||
|
||||
"""
|
||||
fixture = {'id': 2,
|
||||
'name': 'fake image #2',
|
||||
'size': 19,
|
||||
'checksum': None}
|
||||
|
||||
extra_fixture = {'id': 3,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
@@ -470,6 +435,46 @@ class TestRegistryAPI(unittest.TestCase):
|
||||
for image in images:
|
||||
self.assertTrue(image['size'] <= 19 and image['size'] >= 18)
|
||||
|
||||
def test_get_details_filter_property(self):
|
||||
"""Tests that the /images/detail registry API returns list of
|
||||
public images that have a specific custom property
|
||||
|
||||
"""
|
||||
extra_fixture = {'id': 3,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'name': 'fake image #3',
|
||||
'size': 19,
|
||||
'checksum': None,
|
||||
'properties': {'prop_123': 'v a'}}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
extra_fixture = {'id': 4,
|
||||
'status': 'active',
|
||||
'is_public': True,
|
||||
'disk_format': 'ami',
|
||||
'container_format': 'ami',
|
||||
'name': 'fake image #4',
|
||||
'size': 19,
|
||||
'checksum': None,
|
||||
'properties': {'prop_123': 'v b'}}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
req = webob.Request.blank('/images/detail?property-prop_123=v%20a')
|
||||
res = req.get_response(self.api)
|
||||
res_dict = json.loads(res.body)
|
||||
self.assertEquals(res.status_int, 200)
|
||||
|
||||
images = res_dict['images']
|
||||
self.assertEquals(len(images), 1)
|
||||
|
||||
for image in images:
|
||||
self.assertEqual('v a', image['properties']['prop_123'])
|
||||
|
||||
def test_create_image(self):
|
||||
"""Tests that the /images POST registry API creates the image"""
|
||||
fixture = {'name': 'fake public image',
|
||||
|
@@ -222,6 +222,26 @@ class TestRegistryClient(unittest.TestCase):
|
||||
for image in images:
|
||||
self.assertTrue(image['size'] >= 20)
|
||||
|
||||
def test_get_image_details_by_property(self):
|
||||
"""Tests that a detailed call can be filtered by a property"""
|
||||
extra_fixture = {'id': 3,
|
||||
'status': 'saving',
|
||||
'is_public': True,
|
||||
'disk_format': 'vhd',
|
||||
'container_format': 'ovf',
|
||||
'name': 'new name! #123',
|
||||
'size': 19,
|
||||
'checksum': None,
|
||||
'properties': {'p a': 'v a'}}
|
||||
|
||||
glance.registry.db.api.image_create(None, extra_fixture)
|
||||
|
||||
images = self.client.get_images_detailed({'property-p a': 'v a'})
|
||||
self.assertEquals(len(images), 1)
|
||||
|
||||
for image in images:
|
||||
self.assertEquals('v a', image['properties']['p a'])
|
||||
|
||||
def test_get_image(self):
|
||||
"""Tests that the detailed info about an image returned"""
|
||||
fixture = {'id': 1,
|
||||
|
Reference in New Issue
Block a user