ansible/jinja2 get unique subelements

2020-07-27 16:48发布

问题:

I got a list like this:

host_depends:
  - host: abc
    depends:
      - name: item1
      - name: item4
        type: asdf
      - name: item6
  - host: def
    depends:
      - name: item2
      - name: item4
      - name: item6

I need to loop over the unique name of the depends elemnents, so in this example I want to loop over

- item1 
- item2
- item4
- item6

Basically what

debug: var=item.1.name
with_subelements:
  - "{{ host_depends }}"
  - depends

does, but with unique elements only.

How can I get the depends of all host_depends items so I can run a unique filter over them and use them with with_items?

Edit:

I manage to get a list of all depends items like this:

host_depends|map(attribute='depends')|list

But from there, I fail to reduce this list to the name items.

回答1:

If things get too complicated with Ansible and you can't map you mind around it, it's an indicator that it shouldn't be done. Maybe it's possible with some Jinja filters and some set_fact tasks in a loop. But don't, Ansible is not a programming language and should not be used as such. Ansible has two main strengths: readability and extensibility. Don't break the first by ignoring the latter.

with_subelements is in fact a plugin itself, just that it is a core plugin. Just copy it and create your own with_unique_subelements plugin. Here is the code of with_subelements. Line 100 is where the elements are added to the return list. That's where you can hook in and implement a check if that item already was added.

Save your modified version relative to your playbook as lookup_plugins/unique_subelements.py or if you use Ansible 2 you can also store it with the same path inside any role.



回答2:

host_depends|map(attribute='depends')|list

returns a list of lists, as depends is a list. To flatten/combine this list of lists into one list, use the builtin flatten lookup:

lookup('flattened', host_depends|map(attribute='depends')) |map(attribute='name')|unique|list


回答3:

host_depends|map(attribute='depends')|list returns a list of lists, as depends is a list. To flatten/combine this list of lists into one list:

Add this as roles/<rolename>/filter_plugins/filter.py:

from ansible import errors

# This converts a list of lists into a single list
def flattenlist(l):
    try:
        return [item for sublist in l for item in sublist]
    except Exception, e:
        raise errors.AnsibleFilterError('split plugin error: %s' % str(e) )

class FilterModule(object):
    ''' A filter to split a string into a list. '''
    def filters(self):
        return {
            'flattenlist' : flattenlist
        }

And use

host_depends|map(attribute='depends')|flattenlist|map(attribute='name')|unique|list