使用动态Ansible Playbook时,如何在主机间共享facts并对比变量?
问题
需要对比两台动态添加主机(无主机文件,无法通过主机名引用,只能用ansible_play_hosts)上的变量,但共享主机间变量时遇到问题。简化后的Playbook代码如下:
- set_fact: samplevar0: var_from_first_server_in_play when: inventory_hostname == ansible_play_hosts[0] - set_fact: samplevar1: var_from_second_server_in_play when: inventory_hostname == ansible_play_hosts[1] - ansible.builtin.debug: msg="echoing vars from both servers in play {{ hostvars.ansible_play_hosts[0].samplevar0 }} {{ hostvars.ansible_play_hosts[1].samplevar1 }}" - ansible.builtin.debug: msg="vars 0 and 1 are equal. " when: "{{ hostvars.ansible_play_hosts[0].samplevar0 }} == {{ hostvars.ansible_play_hosts[1].samplevar1 }}"
执行后报错,输出如下:
TASK [fact_check_role: set_fact] *************************************
ok: [host2255]TASK [fact_check_role: set_fact] *************************************
ok: [host2892]TASK [fact_check_role : ansible.builtin.debug] ************************
fatal: [llrp107d]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: "hostvars['ansible_play_hosts']" is undefined. "hostvars['ansible_play_hosts']" is undefinedThe error appears to be in '/runner/project/roles/fact_check_role/tasks/preflight_host1.yml': line 195, column 3, but may
be elsewhere in the file depending on the exact syntax problem.The offending line appears to be:
ansible.builtin.debug: msg="echoing vars from both servers in play {{ hostvars.ansible_play_hosts[0].samplevar0 }} {{ hostvars.ansible_play_hosts[1].samplevar1 }}"
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:with_items:
- {{ foo }}Should be written as:
with_items: - "{{ foo }}""}
疑问:是否是嵌套变量的Jinja格式使用错误导致的问题?
原因与解决方法
确实是Jinja2嵌套变量的语法错误导致的问题,核心问题有两个:
hostvars访问方式错误:
hostvars是字典类型,当使用动态变量(比如ansible_play_hosts[0]这种运行时确定的主机名)作为键时,不能用点号.访问,必须用方括号[]形式。否则Ansible会把ansible_play_hosts当成主机名去hostvars里查找,而非解析这个变量的实际值。when条件语法错误:Ansible的
when参数本身处于Jinja2上下文,无需再用{{}}包裹表达式,直接写条件逻辑即可。
修正后的Playbook代码如下:
- set_fact: samplevar0: var_from_first_server_in_play when: inventory_hostname == ansible_play_hosts[0] - set_fact: samplevar1: var_from_second_server_in_play when: inventory_hostname == ansible_play_hosts[1] - ansible.builtin.debug: msg: "echoing vars from both servers in play {{ hostvars[ansible_play_hosts[0]].samplevar0 }} {{ hostvars[ansible_play_hosts[1]].samplevar1 }}" - ansible.builtin.debug: msg: "vars 0 and 1 are equal. " when: hostvars[ansible_play_hosts[0]].samplevar0 == hostvars[ansible_play_hosts[1]].samplevar1
关键修改点说明:
- 将
hostvars.ansible_play_hosts[0]改为hostvars[ansible_play_hosts[0]]:先解析ansible_play_hosts[0]得到第一个主机名,再用该主机名作为键去hostvars字典中获取对应主机的变量。 - 移除when条件中的
{{}}:直接编写变量对比逻辑,Ansible会自动在Jinja2上下文中解析。
内容的提问来源于stack exchange,提问作者CaptainSkyfish

