如何让Ansible清单剧本包含不可达服务器的静态数据?
我将服务器清单存储为多个YML文件,每个服务器对应一个文件,示例内容如下:
--- server_data: - var_hostname: "linuxbox42" var_OS: "Linux" var_OS_distro: "RHEL" var_OS_version: "9.2" (...)
我使用Ansible剧本和Jinja模板在服务器linuxbox01上生成清单CSV文件,相关文件内容如下:
主剧本内容
- name: Create inventory hosts: mylinuxservers ignore_unreachable: true become: true gather_facts: yes roles: - role: linux_create_inventory
输出CSV的任务(属于linux_create_inventory角色)
- name: Output to CSV template: src: inventory.csv.j2 dest: /root/inventory.csv delegate_to: linuxbox01
Jinja2模板(inventory.csv.j2)
hostname,OS,OS_distro,OS_version, (...) {% for h in ansible_play_hosts_all %} {{ h }},{{ hostvars[h]['server_data'][0]['var_OS'] | default('unknown') }},{{ hostvars[h]['ansible_distribution'] | default(hostvars[h]['server_data'][0]['var_OS_distro']) }},{{ hostvars[h]['ansible_distribution_version'] | default(hostvars[h]['server_data'][0]['var_OS_version']) }},{{ (...)
该剧本在安装于linuxbox01的Ansible Tower(AWX)上运行。
遇到的问题
我需要CSV中同时包含Ansible无法连接的服务器,且这些服务器应使用YML文件中的静态数据而非Ansible事实。但当前只要存在不可达服务器,剧本就会全部失败,报错信息如下:
fatal: [linuxbox42 -> linuxbox01]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'server_data'"} fatal: [linuxbox99 -> linuxbox01]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'ansible.vars.hostvars.HostVarsVars object' has no attribute 'server_data'"}
其中linuxbox42可被Ansible连接,linuxbox99不可达。若仅在全部可达的服务器子列表上运行剧本(通过Ansible Tower任务的"Limit:"设置),则CSV可正常生成。
现在需要解决:如何让Ansible同时考虑不可达服务器的YML文件数据?
1. 全局预加载所有服务器的静态YML数据
问题核心是不可达主机的server_data变量未被加载到hostvars中——Ansible默认仅在目标主机执行阶段加载对应主机的变量文件。需要在剧本全局阶段提前加载所有服务器的YML文件,确保无论主机是否可达,变量都存在于hostvars内。
假设静态YML文件存储在inventory_vars/目录下,文件名格式为{{ inventory_hostname }}.yml,修改主剧本如下:
- name: Preload all server static data hosts: localhost tasks: - name: Load each server's YML file include_vars: file: "inventory_vars/{{ item }}.yml" name: server_data_{{ item }} loop: "{{ groups['mylinuxservers'] }}" - name: Assign static data to each host's hostvars set_fact: server_data: "{{ lookup('vars', 'server_data_' + item)['server_data'] }}" delegate_to: "{{ item }}" delegate_facts: true loop: "{{ groups['mylinuxservers'] }}" - name: Create inventory hosts: mylinuxservers ignore_unreachable: true become: true gather_facts: yes roles: - role: linux_create_inventory
2. 优化模板的变量引用逻辑
为避免因变量层级不存在导致报错,需在模板中添加更安全的变量引用逻辑,使用default过滤器覆盖整个嵌套路径的缺失场景。修改后的inventory.csv.j2示例:
hostname,OS,OS_distro,OS_version, (...) {% for h in ansible_play_hosts_all %} {% set svr_data = hostvars[h].get('server_data', [{}])[0] %} {{ h }},{{ svr_data.get('var_OS', 'unknown') }},{{ hostvars[h].get('ansible_distribution', svr_data.get('var_OS_distro', 'unknown')) }},{{ hostvars[h].get('ansible_distribution_version', svr_data.get('var_OS_version', 'unknown')) }},{{ (...)
3. 确认任务执行策略(可选)
确保ignore_unreachable: true参数作用于所有任务,若AWX环境支持,可将剧本策略设置为free,但核心解决措施仍是预加载静态变量。
内容的提问来源于stack exchange,提问作者dr_

