Ansible: Loop over registered output in the next task

ansibleansible-playbookjinja

Ansible version: ansible 2.6.2 python version 2.7.5

I am struggling to loop over registered IP addresses from one task in other using with_items and have a debug output from an ansible playbook which gives this:

The playbook:

- name: Check DVS portgroup from vCenter and allocate IP address from EPG description
  hosts: localhost
  connection: local
  gather_facts: no
  vars:
    nios_provider:
     host: "dns1"
     username: ""
     password: ""

  tasks:
  - name: Find unused IP address from EPG
    local_action: command sh /free.sh "{{ item }}"
    with_items: "DM-SNAP (VLAN-869) in DM AMZ, IP: 11.25.2.0/24 BD: BD-DH-VLAN-869 BD: BD_AZX_NO1, IP: 11.25.3.0/24"
    register: dvs_output

  - name: The IP
    debug: msg={{ dvs_output.results | map(attribute='stdout_lines') | list }}
#    debug: msg={{ dvs_output }}
    register: ip_dns

  - name: return next available IP address for network {{ ip_dns }}
    set_fact:
      ipaddr: "{{ lookup('nios_next_ip', '{{ item }}', num=10, provider=nios_provider) }}"
    with_items: "{{ ip_dns.msg }}"
    ignore_errors: true
    register: lookup

This is the the play output. Here the play fails due to unformatted input from previous registered variable.

    PLAY [Check DVS portgroup from vCenter and allocate IP address from EPG description] ***********************************************************************************************************************

TASK [Find unused IP address from EPG] *********************************************************************************************************************************************************************
changed: [localhost -> localhost] => (item=DM-SNAP (VLAN-869) in DM AMZ, IP: 11.25.2.0/24 BD: BD-DH-VLAN-869 BD: BD_AZX_NO1, IP: 11.25.3.0/24)

TASK [The IP] **********************************************************************************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        [
            "11.25.2.0",
            "11.25.3.0"
        ]
    ]
}

TASK [return next available IP address for network {'msg': [[u'11.25.2.0', u'11.25.3.0']], 'failed': False, 'changed': False}] ***************************************************************************

ok: [localhost] => (item=11.25.2.0)
fatal: [localhost]: FAILED! => {"msg": "Unexpected templating type error occurred on ({{ lookup('nios_next_ip', '{{ item }}', num=10, provider=nios_provider) }}): 'dict' object is not callable"}
...ignoring

TASK [Debug IP] ********************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'ansible_facts'\n\nThe error appears to have been in '/etc/ansible/playbooks/vmware/dev/test_allocate.yml': line 37, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n  - name: Debug IP\n    ^ here\n"}

PLAY RECAP *************************************************************************************************************************************************************************************************
localhost                  : ok=3    changed=1    unreachable=0    failed=1

How can i better make the loop work inside the second task where value of IP is not correctly feeded to {{ item }} and thereby looped ?

Best Answer

What you do is, you register a variable in the first task. That var contains the IPs, then you filter that var to be a list and output it as debug and register that debug output and then you try iterate over the output of the debug. Thats not good.

Why don't you iterate over the filter that you output in the debug? So use:

- name: return next available IP address for network {{ ip_dns }}
  set_fact:
    ipaddr: "{{ lookup('nios_next_ip', '{{ item }}', num=10, provider=nios_provider) }}"
  with_items: "{{ dvs_output.results | map(attribute='stdout_lines') | list }}"

Also - next problem - you iterate over a list and set a variable with a value. So, after that task, there is still one variable ipaddr which contains the last IP. If you want to do something with all the IP addresses, then you should use:

- name: return next available IP address for network {{ ip_dns }}
  include_tasks: loop_over_an_ip.yml
  with_items: "{{ dvs_output.results | map(attribute='stdout_lines') | list }}"

The loop_over_an_ip.yml contains a list of tasks that should be done per IP (which is stored in item). You could rename it via loop_var.

- name: return next available IP address for network {{ ip_dns }}
  include_tasks: loop_over_an_ip.yml
  with_items: "{{ dvs_output.results | map(attribute='stdout_lines') | list }}"
  loop_control:
    loop_var: newip