Repository: ansible/ansible

Issue:
Hostvars files of hosts outside current play is not loaded
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
Hostvars loaded from host_vars/hostname.yml, when hostname is not part of current play

##### ANSIBLE VERSION
```
ansible 2.3.1.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.13 (default, Jan 19 2017, 14:48:08) [GCC 6.3.0 20170118]

```

##### CONFIGURATION
```ini
[defaults]
inventory=.vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory 
```
##### OS / ENVIRONMENT
I run debian/buster.

##### SUMMARY


##### STEPS TO REPRODUCE
I created a project to illustrate the difference between including a host in a play and not.

https://github.com/moozer/ansible-hostvars-test
See readme.md

1. run playbook with all hosts
2. run playbook without some hosts.

##### EXPECTED RESULTS
I expected ansible to include host_vars for all hosts in inventory, not just in current play.


##### ACTUAL RESULTS
Running the ansible-playbook command from here:
https://github.com/moozer/ansible-hostvars-test/blob/master/readme.md

show that when "srv-a" is not part of the play - the hostvars from file are not loaded.

From the documentation: https://docs.ansible.com/ansible/latest/playbooks_variables.html#magic-variables-and-how-to-access-information-about-other-hosts
I have the impression that they should always be loaded.




Discussion:
The removal of "needs_triage", does that mean that you agree there is an issue?

@moozer, no, just means its passed triage phase. But i did forget to ask for more info, can you put actual working examples in 'expected' and 'actual' ? as i cannot reproduce locally what you say happens.
I am using the playbooks and host_vars from here: https://github.com/moozer/ansible-hostvars-test

Expected result:
`ansible-playbook test.yml`followed by `ansible-playbook test.yml -l srv-a`should yield no change.

Actual result:
```
TASK [saving stuff as txt file (locally)] ******************************************************************************************************************
--- before: ./test-srv-a.txt
+++ after: dynamically generated
@@ -25,4 +25,4 @@
 
 host vars from hostvars[ws-a]
 - srv-a Not set
-- ws-a  True
+- ws-a  Not set

changed: [srv-a]
```
variables from `host_vars/ws-a.yml`are not applied to the `hostvars['ws-a'].host_vars_ws_a` variable.
you don't show an inventory, if i have both hosts to inventory i cannot reproduce, the only way i get your results is if the host does not exist.
I am in the test setup using vagrants autogenerated inventory file. 
I get the behavior in non-vagrant projects also.

```
➜ cat .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory 
# Generated by Vagrant

srv-a ansible_ssh_host=192.168.121.139 ansible_ssh_port=22 ansible_ssh_user='vagrant' ansible_ssh_private_key_file='/mnt/storage/moz/git/ansible-hostvars-test/.vagrant/machines/srv-a/libvirt/private_key'
ws-a ansible_ssh_host=192.168.121.147 ansible_ssh_port=22 ansible_ssh_user='vagrant' ansible_ssh_private_key_file='/mnt/storage/moz/git/ansible-hostvars-test/.vagrant/machines/ws-a/libvirt/private_key'

[servers]
srv-a

[workstations]
ws-a

```

For completeness, my ansible.cfg
```
➜ cat ansible.cfg  
[defaults]
inventory=.vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory 

```
i am definitely seeing the same problem since upgrading from 2.3.2.0 to 2.4.
our setup is fundamentally similar, we store ssh keys inside host_vars/hostname, then loop over them to build ssh_known_hosts. since the upgrade, most of our hostvars['otherhost'] information is now missing on all but the host currently being executed on.

Resolving patch:
diff --git a/lib/ansible/vars/manager.py b/lib/ansible/vars/manager.py
--- a/lib/ansible/vars/manager.py
+++ b/lib/ansible/vars/manager.py
@@ -232,22 +232,23 @@ def get_vars(self, play=None, host=None, task=None, include_hostvars=True, inclu
             include_delegate_to=include_delegate_to,
         )
 
+        # default for all cases
+        basedirs = [self._loader.get_basedir()]
+
         if play:
             # first we compile any vars specified in defaults/main.yml
             # for all roles within the specified play
             for role in play.get_roles():
                 all_vars = combine_vars(all_vars, role.get_default_vars())
 
-        basedirs = []
         if task:
             # set basedirs
             if C.PLAYBOOK_VARS_ROOT == 'all':  # should be default
                 basedirs = task.get_search_path()
-            elif C.PLAYBOOK_VARS_ROOT == 'top':  # only option pre 2.3
-                basedirs = [self._loader.get_basedir()]
             elif C.PLAYBOOK_VARS_ROOT in ('bottom', 'playbook_dir'):  # only option in 2.4.0
                 basedirs = [task.get_search_path()[0]]
-            else:
+            elif C.PLAYBOOK_VARS_ROOT != 'top':
+                # preserves default basedirs, only option pre 2.3
                 raise AnsibleError('Unkown playbook vars logic: %s' % C.PLAYBOOK_VARS_ROOT)
 
             # if we have a task in this context, and that task has a role, make


Test patch: