diff --git a/docsite/rst/playbooks_filters.rst b/docsite/rst/playbooks_filters.rst index 7d4ace9c4b..830448655a 100644 --- a/docsite/rst/playbooks_filters.rst +++ b/docsite/rst/playbooks_filters.rst @@ -352,6 +352,39 @@ override those in `b`, and so on. This behaviour does not depend on the value of the `hash_behaviour` 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 diff --git a/lib/ansible/plugins/filter/core.py b/lib/ansible/plugins/filter/core.py index d5e1a12e53..3ab9db5a51 100644 --- a/lib/ansible/plugins/filter/core.py +++ b/lib/ansible/plugins/filter/core.py @@ -339,6 +339,18 @@ def comment(text, style='plain', **kw): str_postfix, 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): ''' Ansible core jinja2 filters ''' @@ -415,4 +427,7 @@ class FilterModule(object): # comment-style decoration 'comment': comment, + + # array and dict lookups + 'extract': extract, } diff --git a/test/integration/roles/test_filters/tasks/main.yml b/test/integration/roles/test_filters/tasks/main.yml index a94bb8c655..af6c5d49de 100644 --- a/test/integration/roles/test_filters/tasks/main.yml +++ b/test/integration/roles/test_filters/tasks/main.yml @@ -68,3 +68,13 @@ - '"0.10 GB" == 102400000|human_readable(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"