geerlingguy / ansible-for-devops

Ansible for DevOps examples.
https://www.ansiblefordevops.com
MIT License
8.45k stars 3.46k forks source link

hostvars confusion #564

Open dbv-rl opened 1 year ago

dbv-rl commented 1 year ago

Hi there,

I have trouble understanding a paragraph around page 98 regarding magic variables with host and group variables.

If you ever need to retrieve a specific host's variables from another host, Ansible provides a magic hostvars variable containing all the defined host variables (from inventory files and any discovered YAML files inside host_vars directories).

# From any host, returns "jane".
{{ hostvars['host1']['admin_user'] }}

I was wondering how I missed that behavior and did a small test:

$ tree
.
├── host_vars
│   ├── localhost.yml
│   └── testhost.yml
└── playbook.yml
$ cat playbook.yml
---
- name: Test playbook
  hosts: all
  tasks:
    - name: localhost output
      debug:
        var: debug_output

    - name: testhost output
      debug:
        var: hostvars['testhost']

$ cat hostvars/localhost.yml
---
debug_output: this comes from the localhost.

$ cat hostvars/testhost.yml
---
debug_output: this comes from the test host.

If I run this with the following ansible-playbook-command, the variable content for testhost.yml is not present.


$ ansible-playbook -i localhost, -c local playbook.yml
....
TASK [localhost output] ******************************************************************************************************
lørdag 14 oktober 2023  19:50:20 +0200 (0:00:00.032)       0:00:00.032 ******** 
lørdag 14 oktober 2023  19:50:20 +0200 (0:00:00.032)       0:00:00.032 ******** 
ok: [localhost] => {
    "debug_output": "this comes from the localhost."
}

TASK [testhost output] *******************************************************************************************************
lørdag 14 oktober 2023  19:50:20 +0200 (0:00:00.025)       0:00:00.058 ******** 
lørdag 14 oktober 2023  19:50:20 +0200 (0:00:00.025)       0:00:00.057 ******** 
ok: [localhost] => {
    "hostvars['testhost']": "VARIABLE IS NOT DEFINED!"
}
...

Am I misreading this particular paragraph?
dglinder commented 1 year ago

TLDR: Running your example on a single machine (localhost) is not going to show the testhost data because it doesn't exist in your single node setup.

When you called the ansible-playbook with the -i localhost, inventory option, you told Ansible to run the playbook on a single machine, "localhost" (i.e. the machine you're running Ansible from).

Ansible then got the variable information from host_vars/localhost.yml and that's what you see in the localhost output play in your playbook.

The second play named testhost output will show the values collected or provided for the host named testhost. Since your inventory file only referenced the single system named localhost, Ansible doesn't have any variables for testhost and reported the "VARIABLE IS NOT DEFINED".

To help you understand this, I would suggest having three systems setup: one as the systems (ex: lab1) you run Ansible from, and the other two (ex: lab2, lab3) as test end points to execute the playbooks against. These can be small VMs running in your lab and able to communicate with each other over SSH (i.e. add lab2 and lab3 into the the /etc/hosts file on lab1). Adjust your host_vars files to be named lab2.yml and lab3.yml, adjust your playbook to display the var: hostvars['lab2'] and var: hostvars['lab3']data, then re-run the playbook with-i lab2,lab3` you'll see the results you expect.

dbv-rl commented 1 year ago

TLDR: Running your example on a single machine (localhost) is not going to show the testhost data because it doesn't exist in your single node setup.

Ofc, my bad. I stopped thinking about the inventory as list of available hosts that would define the whole spectrum of available machines.

The code was working as expected when I ran

ansible-playbook -i localhost,testhost -l localhost -c local playbook.yml

instead. No need to make testhost actually available at all. It would have been a bit concerning to me if Ansible would have loaded all files in all cases, no matter if required or referenced at all.

Sorry for not re-building your lab-suggestion, but that solution was requiring simply less effort on my side. I'm sure it would have worked just as well.

My only comment then would be that the quoted paragraph from the book does not give the context that a machine must be defined in the inventory, in order to resolve the host_vars files as well.

Anyhow, I learned something new today that totally slipped my mind: host-variables of not-addressed hosts are available when included in an inventory.

I think it's up to geerlingguy if he thinks that paragraph could benefit from rephrasing or not.

thanks for the clarification. @dglinder