Search code examples
ansibleansible-2.xansible-inventoryansible-template

Ansible: How to print all the inventory hostnames with serial number in order?


I am a novice to Ansible. I am trying to print all the inventory hosts with serial number (sl.no) using a Jinja template which would be transformed to a .csv file. I have this playbook to ping all the hosts and get the status.

- hosts: '{{ host }}'
  gather_facts: true
  become: true      

  tasks:
    - name: Pinging Host {{ host }}
      action: ping
      register: var_ping

I want to print all hostnames and its ping status with serial order number.

How to achieve this?

Expected output: enter image description here


Solution

  • Use Special Variables ansible_play_hosts and ansible_play_hosts_all. Quoting:

    ansible_play_hosts: List of hosts in the current play run, not limited by the serial. Failed/Unreachable hosts are excluded from this list.

    ansible_play_hosts_all: List of all the hosts that were targeted by the play

    1. Create the serial numbers

    For example, given the inventory

    shell> cat hosts
    test_11
    test_12
    test_13
    test_14
    

    and the abridged result of the ping module. The hosts test_12 and test_14 failed.

    TASK [ping] ****************************************************************
    fatal: [test_12]: FAILED! => changed=false 
    ok: [test_11]
    ok: [test_13]
    fatal: [test_14]: UNREACHABLE! => changed=false
    

    gives

    ansible_play_hosts_all: ['test_11', 'test_12', 'test_13', 'test_14']
    ansible_play_hosts: ['test_11', 'test_13']
    

    Declare the variables

    failed_hosts: "{{ ansible_play_hosts_all|difference(ansible_play_hosts) }}"
    failed_hosts_dict: "{{ dict(failed_hosts|product(['fail'])) }}"
    passed_hosts_dict: "{{ dict(ansible_play_hosts|product(['success'])) }}"
    hosts_dict: "{{ passed_hosts_dict|combine(failed_hosts_dict) }}"
    

    gives

    failed_hosts: ['test_12', 'test_14']
    failed_hosts_dict: {'test_12': 'fail', 'test_14': 'fail'}
    passed_hosts_dict: {'test_11': 'success', 'test_13': 'success'}
    hosts_dict: {'test_11': 'success', 'test_13': 'success', 'test_12': 'fail', 'test_14': 'fail'}
    

    Create and test a template. Fit the sorting to you needs

        - debug:
            msg: |
              Serial Number, Hostname, Ping Status
              {% for k,v in hosts_dict.items()|sort() %}
              {{ loop.index }},{{ k }},{{ v }}
              {% endfor %}
    

    gives

      msg: |-
        Serial Number, Hostname, Ping Status
        1,test_11,success
        2,test_12,fail
        3,test_13,success
        4,test_14,fail
    

    If this is what you want write the file

        - copy:
            dest: /tmp/status.csv
            content: |
              Serial Number, Hostname, Ping Status
              {% for k,v in hosts_dict.items()|sort() %}
              {{ loop.index }},{{ k }},{{ v }}
              {% endfor %}
          delegate_to: localhost
    

    gives

    shell> cat /tmp/status.csv 
    Serial Number, Hostname, Ping Status
    1,test_11,success
    2,test_12,fail
    3,test_13,success
    4,test_14,fail
    

    Example of a complete playbook for testing

    - hosts: all
      gather_facts: false
    
      vars:
    
        failed_hosts: "{{ ansible_play_hosts_all|difference(ansible_play_hosts) }}"
        failed_hosts_dict: "{{ dict(failed_hosts|product(['fail'])) }}"
        passed_hosts_dict: "{{ dict(ansible_play_hosts|product(['success'])) }}"
        hosts_dict: "{{ passed_hosts_dict|combine(failed_hosts_dict) }}"
    
      tasks:
    
        - ping:
    
        - debug:
            msg: |
              ansible_play_hosts_all: {{ ansible_play_hosts_all }}
              ansible_play_hosts: {{ ansible_play_hosts }}
              failed_hosts: {{ failed_hosts }}
              failed_hosts_dict: {{ failed_hosts_dict }}
              passed_hosts_dict: {{ passed_hosts_dict }}
              hosts_dict: {{ hosts_dict }}
          run_once: true
    
        - debug:
            msg: |
              Serial Number, Hostname, Ping Status
              {% for k,v in hosts_dict.items()|sort() %}
              {{ loop.index }},{{ k }},{{ v }}
              {% endfor %}
          run_once: true
    
        - copy:
            dest: /tmp/status.csv
            content: |
              Serial Number, Hostname, Ping Status
              {% for k,v in hosts_dict.items()|sort() %}
              {{ loop.index }},{{ k }},{{ v }}
              {% endfor %}
          delegate_to: localhost
          run_once: true
    

    1. Serial numbers are assigned in the inventory

    For example, given the inventory

    shell> cat hosts
    test_11 sn=1
    test_12 sn=2
    test_13 sn=3
    test_14 sn=4
    

    Declare the variables

    failed_hosts: "{{ ansible_play_hosts_all|difference(ansible_play_hosts) }}"
    failed_hosts_dict: |
      {% for host in failed_hosts %}
      {{ host }}:
        status: fail
        sn: {{ hostvars[host]['sn'] }}
      {% endfor %}
    passed_hosts_dict: |
      {% for host in ansible_play_hosts %}
      {{ host }}:
        status: success
        sn: {{ hostvars[host]['sn'] }}
      {% endfor %}
    hosts_list: "{{ passed_hosts_dict|from_yaml|
                    combine(failed_hosts_dict|from_yaml)|
                    dict2items|
                    sort(attribute='value.sn')}}"
    

    gives

    hosts_list:
      - key: test_11
        value:
          sn: 1
          status: success
      - key: test_12
        value:
          sn: 2
          status: fail
      - key: test_13
        value:
          sn: 3
          status: success
      - key: test_14
        value:
          sn: 4
          status: fail
    

    Write the file

        - copy:
            dest: /tmp/status.csv
            content: |
              Serial Number, Hostname, Ping Status
              {% for i in hosts_list %}
              {{ i.value.sn }},{{ i.key }},{{ i.value.status }}
              {% endfor %}
          delegate_to: localhost
          run_once: true
    

    gives the same result

    shell> cat /tmp/status.csv 
    Serial Number, Hostname, Ping Status
    1,test_11,success
    2,test_12,fail
    3,test_13,success
    4,test_14,fail
    

    Example of a complete playbook for testing

    - hosts: all
      gather_facts: false
    
      vars:
    
        failed_hosts: "{{ ansible_play_hosts_all|difference(ansible_play_hosts) }}"
        failed_hosts_dict: |
          {% for host in failed_hosts %}
          {{ host }}:
            status: fail
            sn: {{ hostvars[host]['sn'] }}
          {% endfor %}
        passed_hosts_dict: |
          {% for host in ansible_play_hosts %}
          {{ host }}:
            status: success
            sn: {{ hostvars[host]['sn'] }}
          {% endfor %}
        hosts_list: "{{ passed_hosts_dict|from_yaml|
                        combine(failed_hosts_dict|from_yaml)|
                        dict2items|
                        sort(attribute='value.sn')}}"
    
      tasks:
    
        - ping:
    
        - debug:
            var: hosts_list
          run_once: true
    
        - debug:
            msg: |
              ansible_play_hosts_all: {{ ansible_play_hosts_all }}
              ansible_play_hosts: {{ ansible_play_hosts }}
              failed_hosts: {{ failed_hosts }}
              failed_hosts_dict: {{ failed_hosts_dict|from_yaml }}
              passed_hosts_dict: {{ passed_hosts_dict|from_yaml }}
              hosts_list: {{ hosts_list }}
          run_once: true
    
        - debug:
            msg: |
              Serial Number, Hostname, Ping Status
              {% for i in hosts_list %}
              {{ i.value.sn }},{{ i.key }},{{ i.value.status }}
              {% endfor %}
          run_once: true
    
        - copy:
            dest: /tmp/status.csv
            content: |
              Serial Number, Hostname, Ping Status
              {% for i in hosts_list %}
              {{ i.value.sn }},{{ i.key }},{{ i.value.status }}
              {% endfor %}
          delegate_to: localhost
          run_once: true