Revision 1716a15d

b/kamaki/cli/commands/image.py
212 212
        prop=KeyValueArgument('filter by property key=value', ('--property')),
213 213
        prop_like=KeyValueArgument(
214 214
            'fliter by property key=value where value is part of actual value',
215
            ('--property-like'))
215
            ('--property-like')),
216 216
    )
217 217

  
218 218
    def _filtered_by_owner(self, detail, *list_params):
......
642 642
class image_compute_list(_init_cyclades, _optional_json):
643 643
    """List images"""
644 644

  
645
    PERMANENTS = ('id', 'name')
646

  
645 647
    arguments = dict(
646 648
        detail=FlagArgument('show detailed output', ('-l', '--details')),
647 649
        limit=IntArgument('limit number listed images', ('-n', '--number')),
648 650
        more=FlagArgument(
649 651
            'output results in pages (-n to set items per page, default 10)',
650 652
            '--more'),
651
        enum=FlagArgument('Enumerate results', '--enumerate')
653
        enum=FlagArgument('Enumerate results', '--enumerate'),
654
        meta=KeyValueArgument(
655
            'filter by metadata key=value (can be repeated)', ('--metadata')),
656
        meta_like=KeyValueArgument(
657
            'filter by metadata key=value (can be repeated)',
658
            ('--metadata-like'))
652 659
    )
653 660

  
661
    def _filter_by_metadata(self, images):
662
        new_images = []
663

  
664
        def like_metadata(meta):
665
            mlike = self['meta_like']
666
            for k, v in mlike.items():
667
                likestr = meta.get(k, '').lower()
668
                if v.lower() not in likestr:
669
                    return False
670
            return True
671

  
672
        for img in images:
673
            meta = img['metadata']
674
            if (
675
                    self['meta'] and set(
676
                        self['meta'].items()).difference(meta.items())) or (
677
                    self['meta_like'] and not like_metadata(meta)):
678
                continue
679
            elif self['detail']:
680
                new_images.append(dict(img))
681
            else:
682
                new_images.append(dict())
683
                for k in set(img).intersection(self.PERMANENTS):
684
                    new_images[-1][k] = img[k]
685
        return new_images
686

  
687
    def _add_name(self, images, key='user_id'):
688
        uuids = self._uuids2usernames(
689
            list(set([img[key] for img in images])))
690
        for img in images:
691
            img[key] += ' (%s)' % uuids[img[key]]
692
        return images
693

  
654 694
    @errors.generic.all
655 695
    @errors.cyclades.connection
656 696
    def _run(self):
657
        images = self.client.list_images(self['detail'])
697
        withmeta = bool(self['meta'] or self['meta_like'])
698
        detail = self['detail'] or withmeta
699
        images = self.client.list_images(detail)
700
        if withmeta:
701
            images = self._filter_by_metadata(images)
702
        if self['detail'] and not self['json_output']:
703
            images = self._add_name(self._add_name(images, 'tenant_id'))
658 704
        kwargs = dict(with_enumeration=self['enum'])
659 705
        if self['more']:
660 706
            kwargs['page_size'] = self['limit'] or 10
b/kamaki/cli/utils/__init__.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
from sys import stdout, stdin
34
from sys import stdout
35 35
from re import compile as regex_compile
36 36
from time import sleep
37 37
from os import walk, path
......
458 458
    for item in list_of_dicts:
459 459
        assert isinstance(item, dict), 'Item %s not a dict' % item
460 460
        item.pop(key_to_remove, None)
461

  
462

  
463
def filter_dicts_by_dict(
464
    list_of_dicts, filters,
465
    exact_match=True, case_sensitive=False):
466
    """
467
    :param list_of_dicts: (list) each dict contains "raw" key-value pairs
468

  
469
    :param filters: (dict) filters in key-value form
470

  
471
    :param exact_match: (bool) if false, check if the filter value is part of
472
        the actual value
473

  
474
    :param case_sensitive: (bool) revers to values only (not keys)
475

  
476
    :returns: (list) only the dicts that match all filters
477
    """
478
    new_dicts = []
479
    for d in list_of_dicts:
480
        if set(filters).difference(d):
481
            continue
482
        match = True
483
        for k, v in filters.items():
484
            dv, v = ('%s' % d[k]), ('%s' % v)
485
            if not case_sensitive:
486
                dv, v = dv.lower(), v.lower()
487
            if not ((
488
                    exact_match and v == dv) or (
489
                    (not exact_match) and v in dv)):
490
                match = False
491
                break
492
        if match:
493
            new_dicts.append(d)
494
    return new_dicts
b/kamaki/cli/utils/test.py
477 477
            remove_from_items([tmp1, tmp2], k)
478 478
            self.assert_dicts_are_equal(tmp1, tmp2)
479 479

  
480
    def test_filter_dicts_by_dict(self):
481
        from kamaki.cli.utils import filter_dicts_by_dict
482

  
483
        dlist = [
484
            dict(k1='v1', k2='v2', k3='v3'),
485
            dict(k1='v1'),
486
            dict(k2='v2', k3='v3'),
487
            dict(k1='V1', k3='V3'),
488
            dict()]
489
        for l, f, em, cs, exp in (
490
                (dlist, dlist[2], True, False, dlist[0:1] + dlist[2:3]),
491
                (dlist, dlist[1], True, False, dlist[0:2] + dlist[3:4]),
492
                (dlist, dlist[1], True, True, dlist[0:2]),
493
                (dlist, {'k3': 'v'}, True, False, []),
494
                (dlist, {'k3': 'v'}, False, False, dlist[0:1] + dlist[2:4]),
495
                (dlist, {'k3': 'v'}, False, True, dlist[0:1] + dlist[2:3]),
496
                (dlist, {'k3': 'v'}, True, True, []),
497
                ):
498
            self.assertEqual(exp, filter_dicts_by_dict(l, f, em, cs))
499

  
480 500

  
481 501
if __name__ == '__main__':
482 502
    from sys import argv

Also available in: Unified diff