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

It will look up the variable on all host in group mysql

This is the code in the template

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

In the inventory he has
  [mysql]
  mysql01
  mysql02
  mysql03
  mysql04

In the first iteration host = mysql01 so he is running
  {{ hostvars['mysql01']['ansible_default_ipv4']['address'] }}

But since gather fact has not been run this will get you the error undefined variable.

As far as I can tell there is no such loop in the template:

[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

As far as I can tell there is no such loop in the template:

It is, i just formatted it to show it more clearly.

[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 %}"

The loop is here.

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

From which message did you get that loop in the template?

See above.

Ah, I missed that line.

Thanks.

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

It will look up the variable on all host in group mysql

This is the code in the template

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

In the inventory he has
[mysql]
mysql01
mysql02
mysql03
mysql04

In the first iteration host = mysql01 so he is running
{{ hostvars[‘mysql01’][‘ansible_default_ipv4’][‘address’] }}

But since gather fact has not been run this will get you the error undefined variable.

Sorry for the late reply. We might have lost track of the original error. mysql0[1-3] are ok and print all variables. Only host mysql04 prints the undefined variable. (see above). Going to do more tests on your suggestions and see further. Included a few of the modifications Uwe pointed out as well. My Ansible version:

[root@awx01 ansible]# rpm -aq|grep -Ei ansible ansible-inventory-grapher-2.4.4-1.el7.noarch ansible-lint-3.4.21-1.el7.noarch ansible-review-0.13.4-1.el7.noarch ansible-doc-2.5.3-1.el7.noarch ansible-2.7.0-1.el7.noarch [root@awx01 ansible]#

Ok. So I removed a couple of tags from the mysql task “mysql : Copy my.cnf global MySQL configuration.” and adjusted the play as follows:

`
[root@awx01 ansible]# vi main.yml

Ok. So I removed a couple of tags from the mysql task "mysql : Copy my.cnf
global MySQL configuration." and adjusted the play as follows:

The playbook is fine, the problem is the limit option your are using on ansible-playbook.

[root@awx01 ansible]# vi main.yml
---
- name: Gather all facts prior to execution
  hosts: mysql
  gather_facts: true
  tasks:
    - debug: msg='{{ inventory_hostname }} has default IP {{
ansible_default_ipv4["address"] }}'
    - template:
        src: test.j2
        dest: /tmp/test.out
  tags: mysql

- name: Install and configure MySQL
  hosts: mysql
  become: true
  roles:
    - mysql
  tags: mysql

But that didn't work. Still got the original error with mysql04. Until I
removed mysql01-3 from the infra file leaving only mysql04:

[mysql]
mysql04

And reran using:

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

You still have the same problem I commented on earlier.

When you run a with --limit, the task and *gather_facts* is only run on host specified in the limit.
So when you in you template try using facts for mysql01-03 they don't exist since you haven't gather them so you get the error message.

So remove you --limit and it will work, the template you have will never work if you specify limit.

Hi Tom,

Can you try a couple of options and post it to this thread please ?
With the same inventory file:

[mysql] mysql01 mysql02 mysql03 mysql04

`
ansible-playbook -i infra --limit mysql02 main.yml --tags “mysql” -v

ansible-playbook -i infra --limit mysql03 main.yml --tags “mysql” -v
`

Thnx,
Stephen

So I’ve made two more empty hosts and called them mysql05 and mysql06 and tested on all 3 (this way I don’t blow away my working cluster). Now I removed the limit flag and run it like this:

`

ansible-playbook -i infra main.yml --tags “mysql” -v

`

Everything worked well and the /etc/my.cnf was populated as expected:

[root@mysql04 ~]# cat /etc/my.cnf [mysqld] datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock user=mysql binlog_format=ROW bind-address="192.168.0.109" 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://192.168.0.109,192.168.0.102,192.168.0.111' wsrep_sst_method=rsync server_id=1 wsrep_node_address="192.168.0.109" wsrep_node_name="mysql04" [mysql_safe] log-error=/var/log/mysqld.log pid-file=/var/run/mysqld/mysqld.pid [root@mysql04 ~]#

Still, running the limit on any one, as Stephen suggested, fails with the following for any host specified by limit as you mentioned:

`
ansible-playbook -i infra --limit mysql05 main.yml --tags “mysql” -v --check

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

`
ansible-playbook -i infra --limit mysql06 main.yml --tags “mysql” -v --check

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

But it seems to work opposite to the way described above (Apologies if I’m misreading). The error is thrown for ANY host that I use the –limit flag on, not the ones that I don’t use the limit on.

I would have expected it to gather facts on the host I’m limiting the run too, not the ones I’m excluding.

Cheers,
Tom

Yes, it's only gathering facts on the host in the limit.

The error message isn't about that it can't find ansible_default_ipv4 on the host in the limit (mysql06), it's about mysql06 cant find ansible_default_ipv4 for host mysql05.

Kool. So it’s really saying (bolded):

ansible-playbook -i infra --limit mysql06 main.yml --tags “mysql” -v --check

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

Makes sense. Ty.

Cheers,
TK