yaobin.wen

Yaobin's Blog

View on GitHub
7 January 2022

Ansible: Understanding the `subelements` lookup (`with_subelements`)

by yaobin.wen

The document of subelements lookup [1] is not clear and has confused people [2]. In this article, I’m going to explain how it works. But also note that the subelements lookup can be replaced by loop and the subelements filter [3].

The key to understanding with_subelements is to firstly understand Cartesian product:

In mathematics, specifically set theory, the Cartesian product of two sets A and B, denoted A x B, is the set of all ordered pairs (a, b) where a is in A and b is in B.

For example, given two sets A and B:

A = { 1, 2 }
B = { 3, 4, 5 }

The Cartesian product of A and B are: (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5).

In Python, itertools.product calculates the Cartesian product of input iterables (not limited to two input iterables). The example above can be implemented as follows:

>>> import itertools
>>> A = set([1, 2])
>>> A
{1, 2}
>>> B = set([3, 4, 5])
>>> B
{3, 4, 5}
>>> list(itertools.product(A, B))
[(1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5)]
>>>

with_subelements essentially calculates the Cartesian product of two lists. This is why it takes two input arguments. The special thing about with_subelements is: its second list is contained as a sub-element in the elements of the first list, hence the lookup name with_subelements. For example [2.1]:

---
- hosts: localhost
  gather_facts: no
  vars:
    families:
      - surname: Smith
        country: US
        children:
          - name: Mike
            age: 4
          - name: Kate
            age: 7
      - surname: Sanders
        country: UK
        children:
          - name: Pete
            age: 12
          - name: Sara
            age: 17

  tasks:
    - name: List children
      debug:
        msg: "Family={{ item.0.surname }} Child={{ item.1.name }} Age={{ item.1.age }}"
      with_subelements:
        - "{{ families }}"
        - children

The first list is families; the second list is children which is a sub-element in every element of the first list. Therefore, with_subelements calculates the Cartesian product of the two lists and produces the following list of pairs:

[
  ({u'country': u'US', u'surname': u'Smith'}, {u'age': 4, u'name': u'Mike'}),
  ({u'country': u'US', u'surname': u'Smith'}, {u'age': 7, u'name': u'Kate'}),
  ({u'country': u'UK', u'surname': u'Sanders'}, {u'age': 12, u'name': u'Pete'}),
  ({u'country': u'UK', u'surname': u'Sanders'}, {u'age': 17, u'name': u'Sara'})
]

So the result messages are:

Family=Smith Child=Mike Age=4
Family=Smith Child=Kate Age=7
Family=Sanders Child=Pete Age=12
Family=Sanders Child=Sara Age=17

Refer to subelements-lookup.yml to run the example code.

References:

Tags: Tech