[WIP] Start of subelements filter (#39829)

* Start of subelements filter

* Add docs for subelements filter
This commit is contained in:
Matt Martz 2018-05-23 10:54:13 -05:00 committed by Brian Coca
parent 46fbfd5d53
commit a5f05c6fc2
2 changed files with 106 additions and 0 deletions

View file

@ -184,6 +184,65 @@ into::
- key: Environment - key: Environment
value: dev value: dev
subelements Filter
``````````````````
.. versionadded:: 2.7
Produces a product of an object, and subelement values of that object, similar to the ``subelements`` lookup::
{{ users|subelements('groups', skip_missing=True) }}
Which turns::
users:
- name: alice
authorized:
- /tmp/alice/onekey.pub
- /tmp/alice/twokey.pub
groups:
- wheel
- docker
- name: bob
authorized:
- /tmp/bob/id_rsa.pub
groups:
- docker
Into::
-
- name: alice
groups:
- wheel
- docker
authorized:
- /tmp/alice/onekey.pub
- wheel
-
- name: alice
groups:
- wheel
- docker
authorized:
- /tmp/alice/onekey.pub
- docker
-
- name: bob
authorized:
- /tmp/bob/id_rsa.pub
groups:
- docker
- docker
An example of using this filter with ``loop``::
- name: Set authorized ssh key, extracting just that data from 'users'
authorized_key:
user: "{{ item.0.name }}"
key: "{{ lookup('file', item.1) }}"
loop: "{{ users|subelements('authorized') }}"
.. _random_filter: .. _random_filter:
Random Number Filter Random Number Filter

View file

@ -473,6 +473,52 @@ def flatten(mylist, levels=None):
return ret return ret
def subelements(obj, subelements, skip_missing=False):
'''Accepts a dict or list of dicts, and a dotted accessor and produces a product
of the element and the results of the dotted accessor
>>> obj = [{"name": "alice", "groups": ["wheel"], "authorized": ["/tmp/alice/onekey.pub"]}]
>>> subelements(obj, 'groups')
[({'name': 'alice', 'groups': ['wheel'], 'authorized': ['/tmp/alice/onekey.pub']}, 'wheel')]
'''
if isinstance(obj, dict):
element_list = list(obj.values())
elif isinstance(obj, list):
element_list = obj[:]
else:
raise AnsibleFilterError('obj must be a list of dicts or a nested dict')
if isinstance(subelements, list):
subelement_list = subelements[:]
elif isinstance(subelements, string_types):
subelement_list = subelements.split('.')
else:
raise AnsibleFilterError('subelements must be a list or a string')
results = []
for element in element_list:
values = element
for subelement in subelement_list:
try:
values = values[subelement]
except KeyError:
if skip_missing:
values = []
break
raise AnsibleFilterError("could not find %r key in iterated item %r" % (subelement, values))
except TypeError:
raise AnsibleFilterError("the key %s should point to a dictionary, got '%s'" % (subelement, values))
if not isinstance(values, list):
raise AnsibleFilterError("the key %r should point to a list, got %r" % (subelement, values))
for value in values:
results.append((element, value))
return results
def dict_to_list_of_dict_key_value_elements(mydict): def dict_to_list_of_dict_key_value_elements(mydict):
''' takes a dictionary and transforms it into a list of dictionaries, ''' takes a dictionary and transforms it into a list of dictionaries,
with each having a 'key' and 'value' keys that correspond to the keys and values of the original ''' with each having a 'key' and 'value' keys that correspond to the keys and values of the original '''
@ -574,4 +620,5 @@ class FilterModule(object):
'extract': extract, 'extract': extract,
'flatten': flatten, 'flatten': flatten,
'dict2items': dict_to_list_of_dict_key_value_elements, 'dict2items': dict_to_list_of_dict_key_value_elements,
'subelements': subelements,
} }