Add an 'extract' filter

At its most basic, this is nothing more than an array or hash lookup,
but when used in conjunction with map, it is very useful. For example,
while constructing an "ssh-keyscan …" command to update known_hosts on
all hosts in a group, one can get a list of IP addresses with:

    groups['x']|map('extract', hostvars, 'ec2_ip_address')|list

This returns hostvars[a].ec2_ip_address, hostvars[b].ec2_ip_address, and
so on. You can even specify an array of keys for a recursive lookup, and
mix string and integer keys depending on what you're looking up:

    ['localhost']|map('extract', hostvars, ['vars','group_names',0])|first
        == hostvars['localhost']['vars']['group_names'][0]
            == 'ungrouped'

Includes documentation and tests.
This commit is contained in:
Abhijit Menon-Sen 2015-11-28 20:09:20 +05:30
parent 5be98ca91e
commit 8cf1815867
3 changed files with 58 additions and 0 deletions

View file

@ -352,6 +352,39 @@ override those in `b`, and so on.
This behaviour does not depend on the value of the `hash_behaviour` This behaviour does not depend on the value of the `hash_behaviour`
setting in `ansible.cfg`. setting in `ansible.cfg`.
.. _extract_filter:
Extracting values from containers
---------------------------------
.. versionadded:: 2.0
The `extract` filter is used to map from a list of indices to a list of
values from a container (hash or array)::
{{ [0,2]|map('extract', ['x','y','z'])|list }}
{{ ['x','y']|map('extract', {'x': 42, 'y': 31})|list }}
The results of the above expressions would be::
['x', 'z']
[42, 31]
The filter can take another argument::
{{ groups['x']|map('extract', hostvars, 'ec2_ip_address')|list }}
This takes the list of hosts in group 'x', looks them up in `hostvars`,
and then looks up the `ec2_ip_address` of the result. The final result
is a list of IP addresses for the hosts in group 'x'.
The third argument to the filter can also be a list, for a recursive
lookup inside the container::
{{ ['a']|map('extract', b, ['x','y'])|list }}
This would return a list containing the value of `b['a']['x']['y']`.
.. _comment_filter: .. _comment_filter:
Comment Filter Comment Filter

View file

@ -339,6 +339,18 @@ def comment(text, style='plain', **kw):
str_postfix, str_postfix,
str_end) str_end)
def extract(item, container, morekeys=None):
from jinja2.runtime import Undefined
value = container[item]
if value is not Undefined and morekeys is not None:
if not isinstance(morekeys, list):
morekeys = [morekeys]
value = reduce(lambda d, k: d[k], morekeys, value)
return value
class FilterModule(object): class FilterModule(object):
''' Ansible core jinja2 filters ''' ''' Ansible core jinja2 filters '''
@ -415,4 +427,7 @@ class FilterModule(object):
# comment-style decoration # comment-style decoration
'comment': comment, 'comment': comment,
# array and dict lookups
'extract': extract,
} }

View file

@ -68,3 +68,13 @@
- '"0.10 GB" == 102400000|human_readable(unit="G")' - '"0.10 GB" == 102400000|human_readable(unit="G")'
- '"0.10 Gb" == 102400000|human_readable(isbits=True, unit="G")' - '"0.10 Gb" == 102400000|human_readable(isbits=True, unit="G")'
- name: Container lookups with extract
assert:
that:
- "'x' == [0]|map('extract',['x','y'])|list|first"
- "'y' == [1]|map('extract',['x','y'])|list|first"
- "42 == ['x']|map('extract',{'x':42,'y':31})|list|first"
- "31 == ['x','y']|map('extract',{'x':42,'y':31})|list|last"
- "'local' == ['localhost']|map('extract',hostvars,'ansible_connection')|list|first"
- "'local' == ['localhost']|map('extract',hostvars,['ansible_connection'])|list|first"
- "'ungrouped' == ['localhost']|map('extract',hostvars,['vars','group_names',0])|list|first"