Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to flatten a lists of lists with Ansible / Jinja2?

My basic problem is that upon creation of a set of aws servers I want to configure them to know about each other.

Upon creation of each server their details are saved in a registered 'servers' var (shown below). What I really want to be able to do after creation is run a task like so:

- name: Add servers details to all other servers
  lineinfile:
    dest: /path/to/configfile
    line: "servername={{ item.1.private_ip }}"
  delegate_to: "{{ item.0.public_dns_name }}"
  with_nested:
    - list_of_servers
    - list_of_servers

Supplying the list twice to 'with_nested' is essential here.

Getting a list of list is easy enough to do with:

"{{ servers.results | map(attribute='tagged_instances') | list }}"

which returns:

[ 
  [ { "private_ip": "ip1", "public_dns_name": "dns1" } , { ... }],
  [ { ... }, { ... } ]
]

but how would you turn this into:

[
  { "private_ip": "ip1", "public_dns_name": "dns1" },
  { ... },
  { ... }, 
  { ... }
]

The 'servers' registered var looks like:

"servers": {
    "changed": true,
    "msg": "All items completed",
    "results": [
        {
            ...
            "tagged_instances": [
                {
                    ...
                    "private_ip": "ip1",
                    "public_dns_name": "dns1",
                    ...
                },
                {
                    ...
                    "private_ip": "ip2",
                    "public_dns_name": "dns2",
                    ...
                }
            ]
        },
        {
            ...
            "tagged_instances": [
                {
                    ...
                    "private_ip": "ip3",
                    "public_dns_name": "dn3",
                    ...
                },
                {
                    ...
                    "private_ip": "ip4",
                    "public_dns_name": "dns4",
                    ...
                }
            ]
        },
        ...
    ]
}

Note: I have a pretty ugly solution by using 'with_flattened' and a debug statement to create a new registered var 'flattened_servers' which I then map over again. But am hoping for a more elegant solution :)

like image 281
TomDotTom Avatar asked Aug 07 '15 10:08

TomDotTom


4 Answers

Jinja2 comes with a built-in filter sum which can be used like:

{{ servers.results | sum(attribute='tagged_instances', start=[]) }} 
like image 185
PChambino Avatar answered Oct 11 '22 17:10

PChambino


A bit late, but starting from ansible 2.5 you can do this:

 "{{ servers.results | map(attribute='tagged_instances') | list | flatten }}"
like image 21
Alex Avatar answered Oct 11 '22 17:10

Alex


You can do list comprehension to convert it to list of dicts.

For example:

- name: Convert
  shell: python -c "print [x for b in {{ servers }}['servers']['results'] for x in b['tagged_instances']]"
  register: my_list_of_dicts

Assuming that {{ servers }} variable holds the entire dictionary (not json).

like image 40
Vor Avatar answered Oct 11 '22 18:10

Vor


Just wanted to list the "ugly" workaround as the python code did not work for me

  - debug: var=item
    with_flattened:
      - "{{ servers.results|map(attribute='tagged_instances')|list }}"
    register: servers_instances_tmp
    no_log: True

  - set_fact: servers_instances="{{ servers_instances_tmp.results|map(attribute='item')|list }}"

  - debug: var=servers_instances
like image 29
Gadelkareem Avatar answered Oct 11 '22 16:10

Gadelkareem