Ansible中使用循环替换字典值失败,如何解决?
vm_name字段失败 我编写了一个Ansible Playbook,用于从Nutanix Prism Central拉取虚拟机列表,捕获vm_name与UUID后,将VM短名称替换为生成的FQDN。但在循环替换字典变量中的vm_name时遇到错误:
"The task includes an option with an undefined variable. The error was: str object has no element AnsibleUndefined(hint=None, obj=missing, name='loop')"
最小可复现代码
--- - name: Test of value replacement hosts: all gather_facts: False vars: clone_vm: - { uuid: "ac1fd960-e19a-48f3-935b-442901ef5fee", vm_name: "server1" } - { uuid: "f44880f6-49aa-4ba3-8cba-403ff06f1c5e", vm_name: "server2" } fqdns: - "mymachine@mycompany.com" - "myothermachine@mycompany.com" tasks: - name: Show variable debug: msg: - "{{ clone_vm }}" - "{{ fqdns }}" - name: Replace vm_name with FQDN set_fact: clone_vm: "{{ clone_vm | map('combine', [{'vm_name': item[loop.index0]}]) }}" loop: "{{ fqdns }}" loop_control: loop_var: item - debug: msg: "{{ clone_vm }}"
若将[{'vm_name': item[loop.index0]}]修改为[{'vm_name': item.[loop.index0]}],会触发另一个错误:
template error while templating string: expected name or number. String: {{ clone_vm | map('combine', [{'vm_name': item.[loop.index0]}]) }}. expected name or number
原数据与期望输出
原数据
- uuid: ac1fd960-e19a-48f3-935b-442901ef5fee vm_name: server1 - uuid: f44880f6-49aa-4ba3-8cba-403ff06f1c5e vm_name: server2
期望输出
- uuid: ac1fd960-e19a-48f3-935b-442901ef5fee vm_name: myserver.mycompany.com - uuid: f44880f6-49aa-4ba3-8cba-403ff06f1c5e vm_name: myotherserver.mycompany.com
解决方案
错误根源在于:过滤器上下文无法访问task的loop变量,且原写法会在每次循环中重复修改整个clone_vm列表,逻辑错误。
可以通过一次性配对两个列表并合并字典的方式解决,无需使用loop循环:
方法1:使用Jinja2模板生成更新后的列表
- name: Replace vm_name with FQDN set_fact: clone_vm: >- {% set updated_vms = [] %} {% for vm, fqdn in clone_vm | zip(fqdns) %} {% set _ = updated_vms.append(vm | combine({'vm_name': fqdn})) %} {% endfor %} {{ updated_vms }}
方法2:使用zip+map过滤器组合
- name: Replace vm_name with FQDN set_fact: clone_vm: "{{ clone_vm | zip(fqdns) | map('lambda x: x[0] | combine({'vm_name': x[1]})) | list }}"
这两种方法都会将clone_vm中的每个VM字典与fqdns中对应位置的FQDN配对,替换vm_name字段后生成新列表,完全符合期望输出。
内容的提问来源于stack exchange,提问作者Andrew Madsen

