Commit 1716a15d authored by Stavros Sachtouris's avatar Stavros Sachtouris
Browse files

Add a filter_dicts_by_dict utils method

Refs: #4220

This is a multipurpose method to be used for CLI filtering
parent 5576a4eb
...@@ -212,7 +212,7 @@ class image_list(_init_image, _optional_json): ...@@ -212,7 +212,7 @@ class image_list(_init_image, _optional_json):
prop=KeyValueArgument('filter by property key=value', ('--property')), prop=KeyValueArgument('filter by property key=value', ('--property')),
prop_like=KeyValueArgument( prop_like=KeyValueArgument(
'fliter by property key=value where value is part of actual value', 'fliter by property key=value where value is part of actual value',
('--property-like')) ('--property-like')),
) )
def _filtered_by_owner(self, detail, *list_params): def _filtered_by_owner(self, detail, *list_params):
...@@ -642,19 +642,65 @@ class image_compute(_init_cyclades): ...@@ -642,19 +642,65 @@ class image_compute(_init_cyclades):
class image_compute_list(_init_cyclades, _optional_json): class image_compute_list(_init_cyclades, _optional_json):
"""List images""" """List images"""
PERMANENTS = ('id', 'name')
arguments = dict( arguments = dict(
detail=FlagArgument('show detailed output', ('-l', '--details')), detail=FlagArgument('show detailed output', ('-l', '--details')),
limit=IntArgument('limit number listed images', ('-n', '--number')), limit=IntArgument('limit number listed images', ('-n', '--number')),
more=FlagArgument( more=FlagArgument(
'output results in pages (-n to set items per page, default 10)', 'output results in pages (-n to set items per page, default 10)',
'--more'), '--more'),
enum=FlagArgument('Enumerate results', '--enumerate') enum=FlagArgument('Enumerate results', '--enumerate'),
'filter by metadata key=value (can be repeated)', ('--metadata')),
'filter by metadata key=value (can be repeated)',
) )
def _filter_by_metadata(self, images):
new_images = []
def like_metadata(meta):
mlike = self['meta_like']
for k, v in mlike.items():
likestr = meta.get(k, '').lower()
if v.lower() not in likestr:
return False
return True
for img in images:
meta = img['metadata']
if (
self['meta'] and set(
self['meta'].items()).difference(meta.items())) or (
self['meta_like'] and not like_metadata(meta)):
elif self['detail']:
for k in set(img).intersection(self.PERMANENTS):
new_images[-1][k] = img[k]
return new_images
def _add_name(self, images, key='user_id'):
uuids = self._uuids2usernames(
list(set([img[key] for img in images])))
for img in images:
img[key] += ' (%s)' % uuids[img[key]]
return images
@errors.generic.all @errors.generic.all
@errors.cyclades.connection @errors.cyclades.connection
def _run(self): def _run(self):
images = self.client.list_images(self['detail']) withmeta = bool(self['meta'] or self['meta_like'])
detail = self['detail'] or withmeta
images = self.client.list_images(detail)
if withmeta:
images = self._filter_by_metadata(images)
if self['detail'] and not self['json_output']:
images = self._add_name(self._add_name(images, 'tenant_id'))
kwargs = dict(with_enumeration=self['enum']) kwargs = dict(with_enumeration=self['enum'])
if self['more']: if self['more']:
kwargs['page_size'] = self['limit'] or 10 kwargs['page_size'] = self['limit'] or 10
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
# interpreted as representing official policies, either expressed # interpreted as representing official policies, either expressed
# or implied, of GRNET S.A. # or implied, of GRNET S.A.
from sys import stdout, stdin from sys import stdout
from re import compile as regex_compile from re import compile as regex_compile
from time import sleep from time import sleep
from os import walk, path from os import walk, path
...@@ -458,3 +458,37 @@ def remove_from_items(list_of_dicts, key_to_remove): ...@@ -458,3 +458,37 @@ def remove_from_items(list_of_dicts, key_to_remove):
for item in list_of_dicts: for item in list_of_dicts:
assert isinstance(item, dict), 'Item %s not a dict' % item assert isinstance(item, dict), 'Item %s not a dict' % item
item.pop(key_to_remove, None) item.pop(key_to_remove, None)
def filter_dicts_by_dict(
list_of_dicts, filters,
exact_match=True, case_sensitive=False):
:param list_of_dicts: (list) each dict contains "raw" key-value pairs
:param filters: (dict) filters in key-value form
:param exact_match: (bool) if false, check if the filter value is part of
the actual value
:param case_sensitive: (bool) revers to values only (not keys)
:returns: (list) only the dicts that match all filters
new_dicts = []
for d in list_of_dicts:
if set(filters).difference(d):
match = True
for k, v in filters.items():
dv, v = ('%s' % d[k]), ('%s' % v)
if not case_sensitive:
dv, v = dv.lower(), v.lower()
if not ((
exact_match and v == dv) or (
(not exact_match) and v in dv)):
match = False
if match:
return new_dicts
...@@ -477,6 +477,26 @@ class UtilsMethods(TestCase): ...@@ -477,6 +477,26 @@ class UtilsMethods(TestCase):
remove_from_items([tmp1, tmp2], k) remove_from_items([tmp1, tmp2], k)
self.assert_dicts_are_equal(tmp1, tmp2) self.assert_dicts_are_equal(tmp1, tmp2)
def test_filter_dicts_by_dict(self):
from kamaki.cli.utils import filter_dicts_by_dict
dlist = [
dict(k1='v1', k2='v2', k3='v3'),
dict(k2='v2', k3='v3'),
dict(k1='V1', k3='V3'),
for l, f, em, cs, exp in (
(dlist, dlist[2], True, False, dlist[0:1] + dlist[2:3]),
(dlist, dlist[1], True, False, dlist[0:2] + dlist[3:4]),
(dlist, dlist[1], True, True, dlist[0:2]),
(dlist, {'k3': 'v'}, True, False, []),
(dlist, {'k3': 'v'}, False, False, dlist[0:1] + dlist[2:4]),
(dlist, {'k3': 'v'}, False, True, dlist[0:1] + dlist[2:3]),
(dlist, {'k3': 'v'}, True, True, []),
self.assertEqual(exp, filter_dicts_by_dict(l, f, em, cs))
if __name__ == '__main__': if __name__ == '__main__':
from sys import argv from sys import argv
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment