How to combine two lists together?

2019-07-18 05:15发布

问题:

I have two lists:

the_list:
  - { name: foo }
  - { name: bar }
  - { name: baz }

and I use a task which gets some value for its every element:

- name: Get values
  shell:
    magic_command {{ item.name }}
  with_items: the_list
  register: spells

from now on I can use the_list and its correspondig values together:

- name: Use both
  shell:
    do_something {{ item.0.name }} {{ item.1.stdout }}
  with_together:
    - "{{ the_list }}"
    - "{{ spells.results }}"

All works fine but it's uncomfortable to use with_together for many tasks and it'll be hard to read that code in a future so I would be more than happy to build merged_list from that which I can use in a simple way. Let say something like this:

merged_list:
 - { name: foo, value: jigsaw }
 - { name: bar, value: crossword }
 - { name: baz, value: monkey }

which makes the puzzle. Anyone can help ?

回答1:

It may be an overkill but you should try to write a custom filter plugin.

Each time you iterates the_list you simple wants to add value to that dict {name: 'foo'} right?

After the update you just want that the new dict has the value like: {name: 'foo', value: 'jigsaw'}

The filter plugin for that it's pretty simple:

def foo(my_list, spells):
    try:
        aux = my_list

        for i in xrange(len(aux)):
            my_list[i].update(spells[i])

        return my_list

    except Exception, e:
        raise errors.AnsibleFilterError('Foo plugin error: %s, arguments=%s' % str(e), (my_list,spells) )

class FilterModule(object):

    def filters(self):
       return {
            'foo' : foo
       }

After adding this python code inside your plugins directory, you can easily call the foo plugin passing the spells list as a parameter:

 - name: Get values
    shell:
      magic_command {{ item.name }}
    with_items: the_list
    register: spells

  - name: Use both
    shell:
      do_something {{ item.name }} {{ item.value }}
    with_items:
      - "{{ the_list | foo(spells.results) }}"

NOTE: The python code it's just an example. Read the ansible documentations about developing filter plugins.



回答2:

I wrote two ansible filters to tackle this problem: zip and todict which are available in my repo at https://github.com/ahes/ansible-filter-plugins

Sample ansible playbook:

- hosts: localhost
  vars:
    users:
      - { name: user1 }
      - { name: user2 }
  tasks:
    - name: Get uids for users
      command: id -u {{ item.name }}
      register: uid_results
      with_items: users

    - set_fact:
        uids: "{{ uid_results.results | map(attribute='stdout') | todict('uid') }}"

    - set_fact:
        users: "{{ users | zip(uids) }}"

    - name: Show users with uids
      debug: var=users

Result would be:

TASK [Show users with uids] ****************************************************
ok: [localhost] => {
    "users": [
        {
            "name": "user1",
            "uid": "1000"
        },
        {
            "name": "user2",
            "uid": "2000"
        }
    ]
}


标签: ansible