Ansible set_fact list using jinja

Viewed 55

I'm trying to learn jinja and Ansible. This is on RHEL 7.9,

ansible 2.9.27
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /bin/ansible
  python version = 2.7.5 (default, May 27 2022, 11:27:32) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]

The only reason I'm performing this exercise is to learn. But I'm baffled. Why does this not produce a list from the found_route_list (which is a list)?

    - name: Populate appended route list
      set_fact:
        found_added_routes: |- 
          {%- set blah = [] %}
            {%- for line in found_route_list %}
              {%- set _ = blah.append(line) %}
            {%- endfor %} 
          {{ blah }}

My playbook starts by gathering the routes on my server:

- name: Get route info
  shell: /usr/sbin/ip route show | sed -e 's/ proto.*//' 
  register: cmdtmp
  changed_when:
    - cmdtmp.stdout | length == 0

- name: Get route list
  set_fact:
    found_route_list: "{{ cmdtmp.stdout.split('\n') }}"

While simply produces a few lines of output, like this:

default via 10.13.99.1 dev bond0.99
10.13.101.0/23 dev p1p1.101
128.1.66.227 via 10.13.101.1 dev p1p1.101

I perform the aforementioned append on it using the set_fact to found_added_routes. Then I debug:

- name: Debug found_route_list
  debug:
    msg: "Found route list: {{ item }}"
  loop: "{{ found_route_list }}"

- name: Debug found_added_routes
  debug:
    msg: "Found: {{ item }}"
  loop: "{{ found_added_routes }}"

Here's my output:

TASK [networks : Debug found_route_list] **********************************************************************************************************************
ok: [server01.example.com] => (item=default via 10.13.99.1 dev bond0.99) => {
    "msg": "Found route list: default via 10.13.99.1 dev bond0.99"
}
ok: [server01.example.com] => (item=10.13.101.0/24 dev p1p1.101) => {
    "msg": "Found route list: 10.13.101.0/24 dev p1p1.101"
}
ok: [server01.example.com] => (item=128.1.66.227 via 10.13.101.1 dev p1p1.101) => {
    "msg": "Found route list: 128.1.66.227 via 10.13.101.1 dev p1p1.101"
}

TASK [networks : Debug found_added_routes] ********************************************************************************************************************
fatal: [server01.example.com]: FAILED! => {"msg": "Invalid data passed to 'loop', it requires a list, got this instead:  [u'default via 10.13.99.1 dev bond0.99', u'10.13.101.0/24 dev p1p1.101', u'128.1.66.227 via 10.13.101.1 dev p1p1.101']. Hint: If you passed a list/dict of just one element, try adding wantlist=True to your lookup invocation or use q/query instead of lookup."}

I tried to create my list like this but I get the same output:

- name: Populate appended route list
  set_fact:
    found_added_routes: "{%- set blah = [] %}
        {%- for line in found_route_list %}
          {%- set _ = blah.append(line) %}
        {%- endfor %} 
      {{ blah }}"

Also, I have seen the syntax in set_fact, where you do something like: set_fact: |- but for the life of me I can't find any documentation or references to it. Could you point me to something that explains it?

Thanks.

1 Answers

The result of a template is always a string. Quoting:

Jinja can generate any text-based format ...

Take a look at the type of the variable found_added_routes

    - debug:
        var: found_added_routes|type_debug

gives

  found_added_routes|type_debug: str

The string is valid YAML. Use filter from_yaml and convert the string to the list. For example,

    - debug:
        var: item
      loop: "{{ found_added_routes|from_yaml }}"

gives (abridged)

  item: default via 10.13.99.1 dev bond0.99
  item: 10.13.101.0/24 dev p1p1.101
  item: 128.1.66.227 via 10.13.101.1 dev p1p1.101
Related