Ansible剧本传递用户输入的库存组变量至后续Play报错求助
问题场景
编写Ansible剧本时,想要实现:
- 提示用户选择一个对应库存组的区域
- 针对选中的库存组执行Play
- 将该区域值设为fact,方便同剧本其他Play调用
库存文件内容
[ams] device1 device2 [usa] device1 device2 [jpn] device1 device2
现有剧本内容
--- - name: Play 1 hosts: "{{ region }}" gather_facts: false connection: network_cli vars_prompt: - name: region prompt: "Region (ams, usa, jpn)" private: no tasks: - name: Make vars persistent set_fact: region: "{{ region }}" - name: check var debug: msg: "{{ region }}" - name: Play 2 hosts: "{{ region }}" gather_facts: false connection: network_cli vars: region: "{{ hostvars['region'] }}" tasks: - name: debug debug: msg: "{{ region }}"
遇到的错误
ERROR! The field 'hosts' has an invalid value, which includes an undefined variable. The error was: {{ hostvars['region'] }}: "hostvars['region']" is undefined. "hostvars['region']" is undefined. {{ hostvars['region'] }}: "hostvars['region']" is undefined. "hostvars['region']" is undefined
我试过用{{ hostvars['region']['region'] }},但还是报同样的错,是不是读取facts的方式有误?
问题原因
set_fact默认把变量绑定到目标主机上,而hostvars的正确用法是hostvars['主机名']['变量名'],你用hostvars['region']是把region当成了主机名,自然找不到。另外Play 2解析hosts字段时,还没确定要连接的主机,hostvars里也没有可用数据。
正确实现方式
方法1:全局定义vars_prompt(最简洁)
把vars_prompt移到剧本最外层,这样所有Play启动前就会获取用户输入,直接共享region变量:
--- vars_prompt: - name: region prompt: "Region (ams, usa, jpn)" private: no - name: Play 1 hosts: "{{ region }}" gather_facts: false connection: network_cli tasks: - name: 检查变量 debug: msg: "{{ region }}" - name: Play 2 hosts: "{{ region }}" gather_facts: false connection: network_cli tasks: - name: 调试输出region变量 debug: msg: "{{ region }}"
方法2:将变量存到localhost的facts中
通过delegate_to: localhost把变量绑定到本地主机,后续Play从hostvars['localhost']读取:
--- - name: Play 1 hosts: "{{ region }}" gather_facts: false connection: network_cli vars_prompt: - name: region prompt: "Region (ams, usa, jpn)" private: no tasks: - name: 把region持久化到localhost set_fact: region: "{{ region }}" delegate_to: localhost run_once: true - name: 检查变量 debug: msg: "{{ region }}" - name: Play 2 hosts: "{{ hostvars['localhost']['region'] }}" gather_facts: false connection: network_cli tasks: - name: 调试输出region变量 debug: msg: "{{ hostvars['localhost']['region'] }}"
方法3:临时文件传递(适合复杂场景)
如果担心localhost不可用,可通过本地临时文件存储变量:
--- - name: Play 1 hosts: "{{ region }}" gather_facts: false connection: network_cli vars_prompt: - name: region prompt: "Region (ams, usa, jpn)" private: no tasks: - name: 将region写入临时文件 copy: content: "{{ region }}" dest: "{{ playbook_dir }}/.region_tmp" delegate_to: localhost run_once: true - name: 检查变量 debug: msg: "{{ region }}" - name: Play 2 hosts: "{{ lookup('file', playbook_dir ~ '/.region_tmp') }}" gather_facts: false connection: network_cli tasks: - name: 调试输出region变量 debug: msg: "{{ lookup('file', playbook_dir ~ '/.region_tmp') }}" - name: 清理临时文件 file: path: "{{ playbook_dir }}/.region_tmp" state: absent delegate_to: localhost run_once: true
内容的提问来源于stack exchange,提问作者hgalvan

