diff --git a/filter/osa-filters.py b/filter/osa-filters.py index d624a520..1071a9c1 100644 --- a/filter/osa-filters.py +++ b/filter/osa-filters.py @@ -317,6 +317,34 @@ def git_link_parse_name(repo): return git_link_parse(repo)['name'] +def get_nested(target_dict, keys): + """Retrieves values through a nested dictionary. + + If any key on the path is missing, return None + + This helps solves convoluted guards in roles/plays such as the following: + + ('openstack_ansible' not in ansible_local or + 'swift' not in ansible_local['openstack_ansible'] or + 'venv_tag' not in ansible_local['openstack_ansible']['swift'] or + ansible_local['openstack_ansible']['swift']['venv_tag'] == swift_venv_tag) + + With this filter, it could be instead written: + ansible_local|get_nested('openstack_ansible.swift.venv_tag') == swift_venv_tag + + """ + + try: + key, next_keys = keys.split('.', 1) + except ValueError: + return target_dict.get(keys, None) + + try: + next_dict = target_dict[key] + except KeyError: + return None + return get_nested(next_dict, next_keys) + class FilterModule(object): """Ansible jinja2 filters.""" @@ -335,5 +363,6 @@ class FilterModule(object): 'filtered_list': filtered_list, 'git_link_parse': git_link_parse, 'git_link_parse_name': git_link_parse_name, - 'deprecated': _deprecated + 'deprecated': _deprecated, + 'get_nested': get_nested } diff --git a/releasenotes/notes/get_nested_filter-b89828586d7e2520.yaml b/releasenotes/notes/get_nested_filter-b89828586d7e2520.yaml new file mode 100644 index 00000000..e4a6a46c --- /dev/null +++ b/releasenotes/notes/get_nested_filter-b89828586d7e2520.yaml @@ -0,0 +1,7 @@ +--- +features: + - The `get_nested` filter has been added, allowing for simplified + value lookups inside of nested dictionaries. + + `ansible_local|get_nested('openstack_ansible.swift')`, for example, + will look 2 levels down and return the result. diff --git a/tests/test-filters.yml b/tests/test-filters.yml index 5ef9bf3a..639ef9d4 100644 --- a/tests/test-filters.yml +++ b/tests/test-filters.yml @@ -120,3 +120,18 @@ - name: Validate deprecated filter assert: that: "deprecated_value == old_var" + + - name: Set test_dict fact + set_fact: + test_dict: + a: + b: + c: d + + - name: Validate get_nested returns value + assert: + that: "{{ test_dict|get_nested('a.b.c') == 'd' }}" + + - name: Validate get_nested returns None on missing key + assert: + that: "{{ test_dict|get_nested('a.c') == None }}"