{"changed": false, "msg": "AnsibleUndefinedVariable: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'ansible_default_ipv4'"}

Hey All,

Receiving the following from Ansible 2.7 and not sure if I should be 
predefining a variable and pointing to this fact first or perhaps this 
is related to something else?  (Relatively new to Ansible)

fatal: [mysql04]: FAILED! => {"changed": false, "msg": 
"AnsibleUndefinedVariable: 'ansible.vars.hostvars.HostVarsVars object' 
has no attribute 'ansible_default_ipv4'"}

[root@awx01 ansible]# ansible --version
ansible 2.7.0
   config file = /etc/ansible/ansible.cfg
   configured module search path = [u'*/root/*.ansible/plugins/modules', 
u'/usr/share/ansible/plugins/modules']
   ansible python module location = /usr/lib/python2.7/site-packages/ansible
   executable location = /bin/ansible
   python version = 2.7.5 (default, Aug  4 2017, 00:39:18) [GCC 4.8.5 
20150623 (Red Hat 4.8.5-16)]
[root@awx01 ansible]#

[root@awx02 mysql]# cat templates/my.cnf.j2
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
binlog_format=ROW
bind-address="{{ ansible_default_ipv4.address }}"
default_storage_engine=innodb
innodb_autoinc_lock_mode=2
innodb_flush_log_at_trx_commit=0
innodb_buffer_pool_size=122M
wsrep_provider=/usr/lib64/galera-3/libgalera_smm.so
wsrep_provider_options="gcache.size=300M; gcache.page_size=300M"
wsrep_cluster_name="galera_cluster1"
wsrep_cluster_address="gcomm://{% for host in 
groups['mysql']%}{{hostvars[host]['ansible_default_ipv4']['address']}}{% 
if not loop.last %},{% endif %}{% endfor %}"
wsrep_sst_method=rsync
server_id=1
wsrep_node_address="{{ ansible_default_ipv4.address }}"
wsrep_node_name="{{ ansible_hostname }}"
[mysql_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
[root@awx02 mysql]#

The fact exists and is defined on the target:

[root@awx01 ansible]# ansible mysql04  -m setup | grep -Ei 192.168.0.109 
-A 2 -B 2
     "ansible_facts": {
         "ansible_all_ipv4_addresses": [
             "192.168.0.109"
         ],
         "ansible_all_ipv6_addresses": [
--
         },
         "ansible_default_ipv4": {
             "address": "192.168.0.109",
             "alias": "eth0",
             "broadcast": "192.168.0.255",
--
             "SHLVL": "2",
             "SSH_CLIENT": "192.168.0.142 49456 22",
             "SSH_CONNECTION": "192.168.0.142 49456 192.168.0.109 22",
             "SSH_TTY": "/dev/pts/1",
             "TERM": "xterm",
--
             "hw_timestamp_filters": [],
             "ipv4": {
                 "address": "192.168.0.109",
                 "broadcast": "192.168.0.255",
                 "netmask": "255.255.255.0",
--
                 "inode_used": 621,
                 "mount": "/n/mds.xyz",
                 "options": 
"rw,relatime,vers=4.1,rsize=8192,wsize=8192,namlen=255,hard,proto=tcp,port=0,timeo=10,retrans=2,sec=sys,clientaddr=192.168.0.10 
,local_lock=none,addr=192.168.0.80",
                 "size_available": 137319276544,
                 "size_total": 137371844608,
[root@awx01 ansible]#

-- 
Cheers,
TK.

Could you show us the play where this happens?

Thanks Uwe!

`

cat main.yml

“HostVarsVars” looks suspicious in the error message. Where does it come from? Find it and check if this is what you want.

‘ansible.vars.hostvars.HostVarsVars object’ has no attribute ‘ansible_default_ipv4’

HTH,

-vlado

Hi,

Thanks Uwe!

>
# cat main.yml
---

-name:Gatherall facts prior to execution
hosts:mysql
gather_facts:yes

-name:Installandconfigure MySQL
hosts:mysql
sudo:yes
roles:
-mysql
tags:mysql

>

First of all: you don't need the first play gathering facts if you don't suppress it in the second.
"gather_facts: true" is default and thus will be executed in every play (something might have changed
due to actions in the last play…)

The above calls this role:

>
# vi tasks/main.yml
# These tasks install the community MySQL Server.

-include_tasks:variables.yml

# Place the my.cnf file on the target hosts.
-name:Copymy.cnf globalMySQLconfiguration.
template:
src:my.cnf.j2
dest:"{{ mysql_config_file }}"
owner:root
group:root
mode:0644
tags:mysql

This looks normal.

In situations like these where I'm unsure at which level of nesting a value can be found I use the following neet trick to get a glimpse of the variable structure:

- name: my_play
   hosts: my_group
   gather_facts: false (to speed up execution. Set to true or omit this line if settings have changed in the last play)
   tasks:
     - (list of preparing tasks, e.g. set_fact)
### debug ####
     - name: dump variable output
       copy:
         dest: /tmp/<variable_name.yaml>
         content: '{{ variable_name | to_nice_yaml }}'
     - pause: (or fail:)
### end debug ###
     - (rest of the play)

With this you'll be able to analyze the structure and see if the attribute / key is actually set for which your playbook or template is looking.

Regards,

  Uwe

Hi,

Thanks Uwe!

cat main.yml


-name:Gatherall facts prior to execution
hosts:mysql
gather_facts:yes

-name:Installandconfigure MySQL
hosts:mysql
sudo:yes
roles:
-mysql
tags:mysql

First of all: you don’t need the first play gathering facts if you don’t suppress it in the second.
“gather_facts: true” is default and thus will be executed in every play (something might have changed
due to actions in the last play…)

I did not have this originally. It was suggested to try it in one of the posts I was reading so I tacked it on but it made no difference.

The above calls this role:

vi tasks/main.yml

These tasks install the community MySQL Server.

-include_tasks:variables.yml

Place the my.cnf file on the target hosts.

-name:Copymy.cnf globalMySQLconfiguration.
template:
src:my.cnf.j2
dest:“{{ mysql_config_file }}”
owner:root
group:root
mode:0644
tags:mysql

This looks normal.

In situations like these where I’m unsure at which level of nesting a value can be found I use the following neet trick
to get a glimpse of the variable structure:

  • name: my_play
    hosts: my_group
    gather_facts: false (to speed up execution. Set to true or omit this line if settings have changed in the last play)
    tasks:
  • (list of preparing tasks, e.g. set_fact)

debug

  • name: dump variable output
    copy:
    dest: /tmp/<variable_name.yaml>
    content: ‘{{ variable_name | to_nice_yaml }}’
  • pause: (or fail:)

end debug

  • (rest of the play)

So I’ve modified the first main.yml as follows:

`

cat main.yml

“HostVarsVars” looks suspicious in the error message. Where does it come from? Find it and check if this is what you want.

It only shows up in the output:

[root@awx01 ansible]# grep -EiR HostVarsVars *
[root@awx01 ansible]#

so thinking this comes from the ansible python code itself. Googling that shows results meaning others receive the same “HostVarsVars” name in the output leading me to believe it’s legit? I found it odd that it prints HostVars (with two Vars) instead of one but maybe that’s not what you were pointing too?

Did you check /tmp on your mysql host? This will copy 'content' to the destination on the target host, not on the host running the playbook.

As the error message state, 'HostVarsVars' is an Python object. Without looking into the source code I suspect it to be some kind of Class inside the HostVars object that holds the actual values you can access via hostvars[inventory_hostname]["key"].

OK, more comments:

So I've modified the first main.yml as follows:

>
# cat main.yml
---

-name:Gatherall facts prior to execution
hosts:mysql
gather_facts:false

Not gathering facts here will lead you to miss many of the ansible_* variables.

tasks:
-name:Dumpansible_default_ipv4 variable output
copy:

The result of the copy module can be found on the target host(s), not on the host running the playbook. But in this case it should already fail as you don't gather facts and thus miss ansible_default_ipv4.

Try the following:

#### test.yaml ####

Did you check /tmp on your mysql host? This will copy ‘content’ to the destination on the target host, not on the host
running the playbook.

I did actually:

[root@mysql04 tmp]# ls -altri /tmp/ansible_default_* ls: cannot access /tmp/ansible_default_*: No such file or directory [root@mysql04 tmp]#

I checked all hosts including the two ansible cluster ones ( awx01, awx02 ). No such file.

`
[root@awx02 mysql]# pwd
/ansible/roles/mysql
[root@awx02 mysql]# cat templates/test.j2

{{ ansible_managed }}

My default IPv4 address is: {{ ansible_default_ipv4[“address”] }}

My other hostvars are:
{{ hostvars[inventory_hostname] | to_nice_yaml }}
[root@awx02 mysql]#
`

`
[root@awx01 ansible]# pwd
/ansible
[root@awx01 ansible]# cat main.yml

Would you mind sending the complete output of

ansible-playbook main.yaml -vvv

?

I got a result now but need to continue this in a bit as I need to step away:

`
[root@mysql04 tmp]# cat /tmp/test.out|grep -Ei ansible_default_ipv4 -A5 -B5
tz_offset: ‘-0500’
weekday: Sunday
weekday_number: ‘0’
weeknumber: ‘44’
year: ‘2018’
ansible_default_ipv4:
address: 192.168.0.109
alias: eth0
broadcast: 192.168.0.255
gateway: 192.168.0.1
interface: eth0
[root@mysql04 tmp]#

`

Still same message:

`
fatal: [mysql04]: FAILED! => {
“changed”: false,
“msg”: “AnsibleUndefinedVariable: ‘ansible.vars.hostvars.HostVarsVars object’ has no attribute ‘ansible_default_ipv4’”
}

`

`
ansible-playbook -i infra --limit mysql04 main.yml --tags “mysql” -vvv

[root@awx01 ansible]# cat main.yml

Does the first line of /tmp/test.out contain the correct IP address? If so, templating is working correctly.

Also please keep in mind that we are no mind readers and stripping away context to make output shorter usually doesn't help. That said please provide the complete output of a run with -vvv.

Hey Uwe,

I hear you and apologies. Was trying to keep it from getting too messy since the output is large.

Link to file is here: http://microdevsys.com/ansible/ansible-vvv.out.txt.gz

Just the same, in case you can’t get to the file, here’s the first 100 lines:

`

TASK [mysql : Print all variables for each remote device] *************************************************************************************************
task path: /ansible/roles/mysql/tasks/variables.yml:21
ok: [mysql04] => {
“hostvars[inventory_hostname]”: {
“__mysql_config_file”: “/etc/my.cnf”,
“ansible_all_ipv4_addresses”: [
“192.168.0.109”
],
“ansible_all_ipv6_addresses”: [
“fe80::250:56ff:fe86:e11b”
],
“ansible_apparmor”: {
“status”: “disabled”
},
“ansible_architecture”: “x86_64”,
“ansible_bios_date”: “04/14/2014”,
“ansible_bios_version”: “6.00”,
“ansible_check_mode”: false,
“ansible_cmdline”: {
“BOOT_IMAGE”: “/vmlinuz-3.10.0-693.21.1.el7.x86_64”,
“LANG”: “en_US.UTF-8”,
“biosdevname”: “0”,
“crashkernel”: “auto”,
“net.ifnames”: “0”,
“quiet”: true,
“rd.lvm.lv”: “centos/swap”,
“rhgb”: true,
“ro”: true,
“root”: “/dev/mapper/centos-root”
},
“ansible_date_time”: {
“date”: “2018-11-04”,
“day”: “04”,
“epoch”: “1541369584”,
“hour”: “17”,
“iso8601”: “2018-11-04T22:13:04Z”,
“iso8601_basic”: “20181104T171304330516”,
“iso8601_basic_short”: “20181104T171304”,
“iso8601_micro”: “2018-11-04T22:13:04.330753Z”,
“minute”: “13”,
“month”: “11”,
“second”: “04”,
“time”: “17:13:04”,
“tz”: “EST”,
“tz_offset”: “-0500”,
“weekday”: “Sunday”,
“weekday_number”: “0”,
“weeknumber”: “44”,
“year”: “2018”
},
“ansible_default_ipv4”: {
“address”: “192.168.0.109”,
“alias”: “eth0”,
“broadcast”: “192.168.0.255”,
“gateway”: “192.168.0.1”,
“interface”: “eth0”,
“macaddress”: “00:50:56:86:e1:1b”,
“mtu”: 1500,
“netmask”: “255.255.255.0”,
“network”: “192.168.0.0”,
“type”: “ether”
},

`

Why do you think the errors is related to mysql04?

Your template you have this code

   {% for host in groups['mysql']%}
   {{hostvars[host]['ansible_default_ipv4']['address']}}{% if not loop.last %},{% endif %}
   {% endfor %}

The host is looping over every host is the group mysql and getting the ansible_default_ipv4, so anyone of them could be the culprit.
So you need to check them all, the easiest way to do that would be:

   ansible mysql -m setup -a 'filter=ansible_default_ipv4'

Because he was running the playbook with this commandline:

ansible-playbook -i infra --limit mysql04 main.yml --tags "mysql" -vvv

There you have the problem.
He has template code that read variable from all the host in group mysql and running the command with limit.
Because of limit the fact gathering will run on mysql04 and not the other hosts in the group so the ['ansible_default_ipv4']['address'] will be undefined for all of them except mysql04.

Sure, but the template will only be used for the one host and as such should not look for variables of other hosts, right?