Ansible能否在Play内条件设置目标主机?现有方案报错求助
需求:当通过-e参数传入的perform_action值为telnet、curl、nslookup、traceroute、get_ip_address其中之一时,Play需运行在localhost;否则运行在remotehosts。
用户执行的命令:
ansible-playbook test.yml -e perform_action='nslookup'
对应的Playbook代码:
- name: "Play 1" hosts: localhost tasks: - set_fact: final_delegate: "{{ 'localhost' if perform_action in 'telnetcurlnslookuptracerouteget_ip_address' else 'remotehosts' }} " - debug: msg: "Play needs to run on {{ final_delegate }}" - name: "Play 2" hosts: "{{ final_delegate }}" tasks: - debug: msg: "Im running on {{ inventory_hostname }}"
执行结果:
- Play1输出正常:
The Play needs to run on localhost - Play2报错:
ERROR! The field 'hosts' has an invalid value, which includes an undefined variable. The error was: 'final_delegate' is undefined
用户疑问:能不能在Play内部实现这个条件设置,还是只能通过-e参数传递条件?
问题根源
set_fact在Play1里设置的变量,默认只存在于localhost的hostvars中。而Play2在解析hosts字段的时候,还没执行任何任务,没法直接读取到Play1里设置的变量,所以会报未定义的错误。
可行的实现方法
方法1:直接在Play2的hosts里写条件逻辑
不用依赖Play1的set_fact,直接把判断逻辑写在Play2的hosts字段里,Ansible解析Play的时候就能算出目标主机:
- name: "Play 2" hosts: "{{ 'localhost' if perform_action in ['telnet', 'curl', 'nslookup', 'traceroute', 'get_ip_address'] else 'remotehosts' }}" tasks: - debug: msg: "Im running on {{ inventory_hostname }}"
注意:原代码里perform_action in 'telnetcurlnslookuptracerouteget_ip_address'是错误的——字符串会被当成字符序列来匹配,必须用列表['telnet', 'curl', ...]才能准确判断值是否在目标集合里。
方法2:通过hostvars读取Play1的变量
如果一定要保留Play1的逻辑,可以在Play2的hosts里通过hostvars['localhost']['final_delegate']来读取localhost上的变量:
- name: "Play 1" hosts: localhost tasks: - set_fact: final_delegate: "{{ 'localhost' if perform_action in ['telnet', 'curl', 'nslookup', 'traceroute', 'get_ip_address'] else 'remotehosts' }}" - debug: msg: "Play needs to run on {{ final_delegate }}" - name: "Play 2" hosts: "{{ hostvars['localhost']['final_delegate'] }}" tasks: - debug: msg: "Im running on {{ inventory_hostname }}"
方法3:通过-e参数传递结果
也可以在执行命令时直接算出目标主机并传入:
ansible-playbook test.yml -e "perform_action='nslookup' final_delegate=$(if [[ 'nslookup' =~ ^(telnet|curl|nslookup|traceroute|get_ip_address)$ ]]; then echo 'localhost'; else echo 'remotehosts'; fi)"
这种情况下,Play2可以直接用{{ final_delegate }}作为hosts的值。
结论
不是只能通过-e传递条件,直接在Play内部写条件表达式或者用hostvars读取变量的方式都能实现需求。
内容的提问来源于stack exchange,提问作者Ashar

