Search code examples
pythonansiblejinja2

How to iterate every two elements of a list in an Ansible loop?


I have list variable with disks names which сan be off variable amount - 4, 8, 16 etc. elements, but even and I want to print every two disks in loop at all iteration. How I can do this?

---
- hosts: all

  vars:

    disks:
      - sdc
      - sdd
      - sde
      - sdf
  
  tasks:
   - name: Iterate every two elements (disks) in loop
     shell: echo '/dev/sdc + /dev/sdd' (and in second iteration of loop must be echo '/dev/sde + /dev/sdf')
     loop: "{{ disks }}"

Solution

  • I have a list with a variable amount of elements (4, 8 or 16, but even) and I want to print chunks of two in a loop at every iteration.

    Although Ansible isn't a programming language such construct is possible in example in the following way

    ---
    - hosts: localhost
      become: false
      gather_facts: false
    
      vars:
    
        disks:
          - sdc
          - sdd
          - sde
          - sdf
          - sdg
          - sdh
          - sdi
          - sdj
    
      tasks:
    
      - name: Input validation
        assert:
          that:
            - disks | length != 0
            - disks | length >= 4
            - disks | length <= 16
            - disks | length is even
          success_msg: "PASSED: List length is OK"
          fail_msg: "FAILED: List length doesn't fulfill requirements!"
    
      - name: Iterate every two elements (disks) in loop
        debug:
          msg: "/dev/{{ disks[item] }} and /dev/{{ disks[item + 1] }}"
        loop: "{{ range(0, disks | length, 2) | list }}"
    

    will result into an output of

    TASK [Input validation] ********************************
    ok: [localhost] => changed=false
      msg: 'PASSED: List length is OK'
    
    TASK [Iterate every two elements (disks) in loop] ******
    ok: [localhost] => (item=0) =>
      msg: /dev/sdc and /dev/sdd
    ok: [localhost] => (item=2) =>
      msg: /dev/sde and /dev/sdf
    ok: [localhost] => (item=4) =>
      msg: /dev/sdg and /dev/sdh
    ok: [localhost] => (item=6) =>
      msg: /dev/sdi and /dev/sdj
    

    It seemed easier for me to loop over a range of integers in steps and use them to catch the list elements directly, instead of iterating over the list elements itself and mess around with ansibe_loop.index.

    Further Documentation


    To take into account the comment about How do I split a list into equally-sized chunks? and other examples, another approach is simply

      - name: Iterate every two elements (disks) in loop
        debug:
          msg: "/dev/{{ item | first  }} and /dev/{{ item | last }}"
        loop: "{{ disks | batch(2) }}"
    

    resulting into an output of

    TASK [Iterate every two elements (disks) in loop] ******
    ok: [localhost] => (item=[u'sdc', u'sdd']) =>
      msg: /dev/sdc and /dev/sdd
    ok: [localhost] => (item=[u'sde', u'sdf']) =>
      msg: /dev/sde and /dev/sdf
    ok: [localhost] => (item=[u'sdg', u'sdh']) =>
      msg: /dev/sdg and /dev/sdh
    ok: [localhost] => (item=[u'sdi', u'sdj']) =>
      msg: /dev/sdi and /dev/sdj
    

    Further Documentation