There are three parts to this use case: 1) Manage the inventory, 2) Collect client_hostname and client_ip, and 3) Report to the central server.
1. Manage the inventory
There are many options on how to manage the inventory and collect client_hostname and client_ip. For example, use Adding ranges of hosts if you plan to create hundreds of hosts with simple names like host001, hosts002, ..., host999
. For example, in the inventory below add central_server, localhost for simplicity, and hundred hosts in the group test
shell> cat inventory/01-hosts
central_server ansible_host=localhost
[test]
host[001:100]
[test:vars]
ansible_connection=ssh
ansible_user=admin
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo
ansible_python_interpreter=/usr/local/bin/python3.8
Briefly test the inventory
- hosts: all
gather_facts: false
tasks:
- debug:
var: ansible_play_hosts|length
run_once: true
give abridged
ansible_play_hosts|length: '101'
Run the command below if you want to display the complete inventory
shell> ansible-inventory -i inventory --list --yaml
Then there is plenty of options for how to select the hosts. See Patterns: targeting hosts and groups. For example, limit the inventory to particular hosts or groups. Test it with the simple playbook below
shell> cat pb1.yml
- hosts: all
gather_facts: false
tasks:
- debug:
var: inventory_hostname
give
shell> ansible-playbook -i inventory pb1.yml -l host001,host002
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [host001] =>
inventory_hostname: host001
ok: [host002] =>
inventory_hostname: host002
PLAY RECAP ***********************************************************************************
host001: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host002: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Use the inventory plugin constructed if you want to limit the inventory to a larger group of hosts. See
shell> ansible-doc -t inventory constructed
For example, the extra variable count_hosts is used in the example below to create the group my_group comprising hosts limited by the value of this variable
shell> cat inventory/02-constructed.yml
plugin: constructed
strict: true
use_extra_vars: true
compose:
my_group_count: count_hosts|default(0)
groups:
my_group: inventory_hostname[-2:]|int < my_group_count|int
Test it
shell> ansible-playbook -i inventory pb.yml -e count_hosts=10 -l my_group
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [host001] =>
ansible_play_hosts|length: '11'
PLAY RECAP ***********************************************************************************
host001: ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Run the command below if you want to display the complete inventory
shell> ansible-inventory -i inventory -e count_hosts=10 --list --yaml
With the help of the constructed plugin, you can create complex conditions. For example, limit the hosts to a particular interval and create the group my_group2
shell> cat inventory/02-constructed.yml
plugin: constructed
strict: true
use_extra_vars: true
compose:
my_group_count: count_hosts|default(0)
my_group_start: start_hosts|default(0)
my_group_stop: stop_hosts|default(0)
groups:
my_group1: inventory_hostname[-2:]|int < my_group_count|int
my_group2: inventory_hostname[-2:]|int >= my_group_start|int and
inventory_hostname[-2:]|int < my_group_stop|int
Test it
shell> ansible-playbook -i inventory pb1.yml -e start_hosts=10 -e stop_hosts=15 -l my_group2
PLAY [all] ***********************************************************************************
TASK [debug] *********************************************************************************
ok: [host010] =>
inventory_hostname: host010
ok: [host011] =>
inventory_hostname: host011
ok: [host012] =>
inventory_hostname: host012
ok: [host013] =>
inventory_hostname: host013
ok: [host014] =>
inventory_hostname: host014
...
2. Collect client_hostname and client_ip
You can use either the module setup or collect the facts on your own. Because of the portability setup should be preferred.
See the module setup on how to gathers facts about remote hosts. For example, the playbook below gathers facts about the machine and network and creates the variables client_hostname and client_ip
shell> cat pb2.yml
- hosts: all
gather_facts: false
tasks:
- setup:
gather_subset:
- machine
- network
- set_fact:
client_hostname: "{{ ansible_hostname }}"
- debug:
var: client_hostname
- debug:
var: ansible_default_ipv4
- debug:
var: ansible_all_ipv4_addresses
- set_fact:
client_ip: "{{ ansible_all_ipv4_addresses|last }}"
- debug:
var: client_ip
give
shell> ansible-playbook -i inventory -l host011 pb2.yml
PLAY [all] ***********************************************************************************
TASK [setup] *********************************************************************************
ok: [host011]
TASK [set_fact] ******************************************************************************
ok: [host011]
TASK [debug] *********************************************************************************
ok: [host011] =>
client_hostname: test_11
TASK [debug] *********************************************************************************
ok: [host011] =>
ansible_default_ipv4: {}
TASK [debug] *********************************************************************************
ok: [host011] =>
ansible_all_ipv4_addresses:
- 10.1.0.61
TASK [set_fact] ******************************************************************************
ok: [host011]
TASK [debug] *********************************************************************************
ok: [host011] =>
client_ip: 10.1.0.61
PLAY RECAP ***********************************************************************************
host011: ok=7 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The structure and format of the facts may differ among the operating systems.
You can collect the facts on your own. For example, the playbook below
shell> cat pb3.yml
- hosts: all
gather_facts: false
tasks:
- name: Grab client hostname
command: cat /etc/hostname
register: out
- set_fact:
client_hostname: "{{ out.stdout }}"
- debug:
var: client_hostname
- name: Grab client IP address
shell: hostname -i | sed -n '1 p'
register: out
- set_fact:
client_ip: "{{ out.stdout|split|last }}"
- debug:
var: client_ip
give running on Linux
shell> ansible-playbook -i inventory -l central_server pb3.yml
PLAY [all] ***********************************************************************************
TASK [Grab client hostname] ******************************************************************
changed: [central_server]
TASK [set_fact] ******************************************************************************
ok: [central_server]
TASK [debug] *********************************************************************************
ok: [central_server] =>
client_hostname: central_server
TASK [Grab client IP address] ****************************************************************
changed: [central_server]
TASK [set_fact] ******************************************************************************
ok: [central_server]
TASK [debug] *********************************************************************************
ok: [central_server] =>
client_ip: 10.1.0.22
PLAY RECAP ***********************************************************************************
central_server: ok=6 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The output of the utilities may differ among the operating systems.
3) Report to the central server
Use Jinja to create the structure. Run the task once and delegate it to the central_server
shell> cat pb4.yml
- hosts: all
gather_facts: false
tasks:
- setup:
gather_subset:
- machine
- network
- set_fact:
client_hostname: "{{ ansible_hostname }}"
client_ip: "{{ ansible_all_ipv4_addresses|last }}"
- copy:
dest: /tmp/test_host_ip.txt
content: |
{% for host in ansible_play_hosts %}
{{ hostvars[host]['client_hostname'] }} - {{ hostvars[host]['client_ip'] }}
{% endfor %}
run_once: true
delegate_to: central_server
give
shell> ansible-playbook -i inventory -l host011,host013 pb4.yml
PLAY [all] ***********************************************************************************
TASK [setup] *********************************************************************************
ok: [host013]
ok: [host011]
TASK [set_fact] ******************************************************************************
ok: [host011]
ok: [host013]
TASK [copy] **********************************************************************************
changed: [host011 -> central_server(localhost)]
PLAY RECAP ***********************************************************************************
host011: ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host013: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The playbook created the file at central_server
shell> cat /tmp/test_host_ip.txt
test_11 - 10.1.0.61
test_13 - 10.1.0.63
Use the module lineinfile if you want to add lines to the file. The playbook below is idempotent
shell> cat pb5.yml
- hosts: all
gather_facts: false
tasks:
- setup:
gather_subset:
- machine
- network
- set_fact:
client_hostname: "{{ ansible_hostname }}"
client_ip: "{{ ansible_all_ipv4_addresses|last }}"
- lineinfile:
path: /tmp/test_host_ip.txt
line: |-
{{ hostvars[item]['client_hostname'] }} - {{ hostvars[item]['client_ip'] }}
loop: "{{ ansible_play_hosts }}"
run_once: true
delegate_to: central_server
There will be no changes when you run it repeatedly on the same hosts
shell> ansible-playbook -i inventory -l host011,host013 pb5.yml
PLAY [all] ***********************************************************************************
TASK [setup] *********************************************************************************
ok: [host011]
ok: [host013]
TASK [set_fact] ******************************************************************************
ok: [host011]
ok: [host013]
TASK [lineinfile] ****************************************************************************
ok: [host011 -> central_server(localhost)] => (item=host011)
ok: [host011 -> central_server(localhost)] => (item=host013)
PLAY RECAP ***********************************************************************************
host011: ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
host013: ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
A new line(s) will be added to the file if the playbook runs on new host(s)
shell> ansible-playbook -i inventory -l central_server pb5.yml
PLAY [all] ***********************************************************************************
TASK [setup] *********************************************************************************
ok: [central_server]
TASK [set_fact] ******************************************************************************
ok: [central_server]
TASK [lineinfile] ****************************************************************************
changed: [central_server] => (item=central_server)
PLAY RECAP ***********************************************************************************
central_server: ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
The new line was appended to the file
shell> cat /tmp/test_host_ip.txt
test_11 - 10.1.0.61
test_13 - 10.1.0.63
central_server - 10.1.0.184