Inner Loop in Ansible

Viewed 39

I have read the docs on with_nested and loops etc but not sure that they apply to my scenario.

My structure is:
org
-- org has networks
---- networks have devices
------ devices have licenses

The goal is to delete my orgs. This can only occur when all the devices have been unlicensed, deleted, and networks delete.

Therefore what I need to do is grab a list of orgs.
-- Then loop thru that list and using the org_id get all the networks
---- For each network I then get the list of devices.
------ Then loop thru the list of devices and delete license keys
------ Delete device.
---- Delete network
-- Delete org
loop

With there only being the one item.x then not sure how to get the item of the inner loops.

Could add code but would look like crap as I simply can't visualize this in my head.

1 Answers

There are more options. For example, given the data

  orgs:
    o1:
      n1:
        d1:
          license: k1
        d2:
          license: k2
      n2:
        d3:
          license: k3
    o2:
      n3:
        d4:
          license: k4
        d5:
          license: k5

  1. Use to_paths lookup
  orgs_paths: "{{ lookup('ansible.utils.to_paths', orgs) }}"

gives

  orgs_paths:
    o1.n1.d1.license: k1
    o1.n1.d2.license: k2
    o1.n2.d3.license: k3
    o2.n3.d4.license: k4
    o2.n3.d5.license: k5

Then, declare the lists

  _key: "{{ orgs_paths.keys()|list }}"
  _dev: "{{ _key|map('splitext')|map('first')|unique }}"
  _net: "{{ _dev|map('splitext')|map('first')|unique }}"
  _org: "{{ _net|map('splitext')|map('first')|unique }}"
  _arr: "{{ item.split('.') }}"

and delete the tree

    - debug:
        msg: "Delete license: {{ orgs_paths[item] }} for {{ _arr[0:3] }}"
      loop: "{{ _key }}"

    - debug:
        msg: "Delete device: {{ _arr.2  }} for {{ _arr[0:2] }}"
      loop: "{{ _dev }}"

    - debug:
        msg: "Delete network: {{ _arr.1  }} for {{ _arr[0:1] }}"
      loop: "{{ _net }}"

    - debug:
        msg: "Delete org: {{ item  }}"
      loop: "{{ _org }}"

    - debug:
        msg: "Delete orgs"

gives (abridged)

  msg: 'Delete license: k1 for [''o1'', ''n1'', ''d1'']'
  msg: 'Delete license: k2 for [''o1'', ''n1'', ''d2'']'
  msg: 'Delete license: k3 for [''o1'', ''n2'', ''d3'']'
  msg: 'Delete license: k4 for [''o2'', ''n3'', ''d4'']'
  msg: 'Delete license: k5 for [''o2'', ''n3'', ''d5'']'
  msg: 'Delete device: d1 for [''o1'', ''n1'']'
  msg: 'Delete device: d2 for [''o1'', ''n1'']'
  msg: 'Delete device: d3 for [''o1'', ''n2'']'
  msg: 'Delete device: d4 for [''o2'', ''n3'']'
  msg: 'Delete device: d5 for [''o2'', ''n3'']'
  msg: 'Delete network: n1 for [''o1'']'
  msg: 'Delete network: n2 for [''o1'']'
  msg: 'Delete network: n3 for [''o2'']'
  msg: 'Delete org: o1'
  msg: 'Delete org: o2'
  msg: Delete orgs

Example of a complete playbook

- hosts: localhost

  vars:

    orgs:
      o1:
        n1:
          d1: {license: k1}
          d2: {license: k2}
        n2:
          d3: {license: k3}
      o2:
        n3:
          d4: {license: k4}
          d5: {license: k5}

    orgs_paths: "{{ lookup('ansible.utils.to_paths', orgs) }}"
    _key: "{{ orgs_paths.keys()|list }}"
    _dev: "{{ _key|map('splitext')|map('first')|unique }}"
    _net: "{{ _dev|map('splitext')|map('first')|unique }}"
    _org: "{{ _net|map('splitext')|map('first')|unique }}"
    _arr: "{{ item.split('.') }}"

  tasks:
    - debug:
        msg: "Delete license: {{ orgs_paths[item] }} for {{ _arr[0:3] }}"
      loop: "{{ _key }}"
    - debug:
        msg: "Delete device: {{ _arr.2  }} for {{ _arr[0:2] }}"
      loop: "{{ _dev }}"
    - debug:
        msg: "Delete network: {{ _arr.1  }} for {{ _arr[0:1] }}"
      loop: "{{ _net }}"
    - debug:
        msg: "Delete org: {{ item  }}"
      loop: "{{ _org }}"
    - debug:
        msg: "Delete orgs"

  1. The next option is the inclusion of tasks in loops. For example, create files
shell> cat del_dev.yml
- block:
    - debug:
        msg: >-
          Delete license {{ item.value }} for
          [{{ item_org.key }}, {{ item_net.key }}, {{ item_dev.key }}]
      loop: "{{ item_dev.value|dict2items }}"
    - debug:
        msg: >-
          Delete device {{ item_dev.key }} for
          [{{ item_org.key }}, {{ item_net.key }}]
shell> cat del_net.yml
- block:
    - include_tasks: del_dev.yml
      loop: "{{ item_net.value|dict2items }}"
      loop_control:
        loop_var: item_dev
    - debug:
        msg: >-
          Delete network {{ item_net.key }} for
          [{{ item_org.key }}]
shell> cat del_org.yml
- block:
    - include_tasks: del_net.yml
      loop: "{{ item_org.value|dict2items }}"
      loop_control:
        loop_var: item_net
    - debug:
        msg: >-
          Delete org {{ item_org.key }}

Recursively include the tasks in a playbook

- hosts: localhost

  vars:

    orgs:
      o1:
        n1:
          d1: {license: k1}
          d2: {license: k2}
        n2:
          d3: {license: k3}
      o2:
        n3:
          d4: {license: k4}
          d5: {license: k5}

  tasks:

    - block:
        - include_tasks: del_org.yml
          loop: "{{ orgs|dict2items }}"
          loop_control:
            loop_var: item_org
        - debug:
            msg: Delete orgs

gives (abridged)

  msg: Delete license k1 for [o1, n1, d1]
  msg: Delete device d1 for [o1, n1]
  msg: Delete license k2 for [o1, n1, d2]
  msg: Delete device d2 for [o1, n1]
  msg: Delete network n1 for [o1]
  msg: Delete license k3 for [o1, n2, d3]
  msg: Delete device d3 for [o1, n2]
  msg: Delete network n2 for [o1]
  msg: Delete org o1
  msg: Delete license k4 for [o2, n3, d4]
  msg: Delete device d4 for [o2, n3]
  msg: Delete license k5 for [o2, n3, d5]
  msg: Delete device d5 for [o2, n3]
  msg: Delete network n3 for [o2]
  msg: Delete org o2
  msg: Delete orgs

  1. Getting the structure on the fly makes the code simpler. For example, let's imagine the structure is the same as before. As the first step in each loop, we find the list of org, their networks, and devices. This is the meaning of the argument in the loop statement. In real life, you would probably get the lists in a separate task before the loop. Create files
shell> cat del_dev.yml
- block:
    - debug:
        msg: >-
          Delete license {{ _item }} for
          [{{ item_org }}, {{ item_net }}, {{ item_dev }}]
      vars:
        _item: "{{ orgs[item_org][item_net][item_dev]['license'] }}"
    - debug:
        msg: >-
          Delete device {{ item_dev }} for
          [{{ item_org }}, {{ item_net }}]
shell> cat del_net.yml
- block:
    - include_tasks: del_dev.yml
      loop: "{{ orgs[item_org][item_net].keys()|list }}"
      loop_control:
        loop_var: item_dev
    - debug:
        msg: >-
          Delete network {{ item_net }} for
          [{{ item_org }}]
shell> cat del_org.yml
- block:
    - include_tasks: del_net.yml
      loop: "{{ orgs[item_org].keys()|list }}"
      loop_control:
        loop_var: item_net
    - debug:
        msg: >-
          Delete org {{ item_org }}

The playbook below gives the same results

- hosts: localhost

  vars:

    orgs:
      o1:
        n1:
          d1: {license: k1}
          d2: {license: k2}
        n2:
          d3: {license: k3}
      o2:
        n3:
          d4: {license: k4}
          d5: {license: k5}

  tasks:

    - block:
        - include_tasks: del_org.yml
          loop: "{{ orgs.keys()|list }}"
          loop_control:
            loop_var: item_org
        - debug:
            msg: Delete orgs

Q: "How to propagate the org_id due to only a single item?"

A: Simply put this single item into the list. For example

- hosts: localhost

  vars:

    orgs:
      o1:
        n1:
          d1: {license: k1}
          d2: {license: k2}
        n2:
          d3: {license: k3}
      o2:
        n3:
          d4: {license: k4}
          d5: {license: k5}

  tasks:

    - block:
        - include_tasks: del_org.yml
          loop:
            - o2
          loop_control:
            loop_var: item_org
        - debug:
            msg: Delete orgs

gives

  msg: Delete license k4 for [o2, n3, d4]
  msg: Delete device d4 for [o2, n3]
  msg: Delete license k5 for [o2, n3, d5]
  msg: Delete device d5 for [o2, n3]
  msg: Delete network n3 for [o2]
  msg: Delete org o2
  msg: Delete orgs
Related