ansible - Ansible 内联 jinja 模板化到列表
问题描述
我不明白为什么这行不通。我正在使用 jinja 动态生成要传递给 vmware_guest 模块的列表,因此我可以在主机或组变量中决定是否要添加其他磁盘。我正在使用 vmware_disk_info 模块来查找模板磁盘的大小,然后添加我将在组 var 中定义的任何其他磁盘。在我看来,输出的列表不是列表????
---
- name: "Get facts for named template"
vmware_guest_disk_info:
hostname: "{{ vcenter_server }}"
username: "{{ vcenter_user }}"
password: "{{ vcenter_pass }}"
validate_certs: False
datacenter: "{{ datacenter_name }}"
name: "{{ template_name }}"
register: template_disk
delegate_to: localhost
- name: "Define new disk structure"
set_fact:
vm_disks: >-
[{% for disk in (template_disk.guest_disk_info|dictsort) %}{
'size_kb': {{ disk[1].capacity_in_kb }},
'datastore': {{ datastore_name }}},
{% endfor %}
{% for disk in additional_disks|default([]) %}{
{% if disk.size_gb is defined %}'size_gb': {{ disk.size_gb }},{% endif %}
{% if disk.size_mb is defined %}'size_mb': {{ disk.size_mb }},{% endif %}
{% if disk.size_kb is defined %}'size_kb': {{ disk.size_kb }},{% endif %}
'datastore': {{ datastore_name }}},
{% endfor %}]
delegate_to: localhost
- name: Clone the template
vmware_guest:
hostname: "{{ vcenter_server }}"
username: "{{ vcenter_user }}"
password: "{{ vcenter_pass }}"
validate_certs: False
name: "{{ inventory_hostname }}"
template: "{{ template_name }}"
datacenter: "{{ datacenter_name }}"
folder: "/{{ datacenter_name }}/vm/{{ folder_name }}"
cluster: "{{ cluster_name }}"
datastore: "{{ datastore_name }}"
resource_pool: "{{ resource_pool_name }}"
disk: "{{ vm_disks }}"
hardware:
memory_gb: "{{ mem_size_gb }}"
num_cpu: "{{ cpu_size }}"
networks:
- name: "{{ network_name }}"
ip: "{{ ansible_host }}"
netmask: "{{ network_mask }}"
gateway: "{{ network_gw }}"
type: static
customization:
hostname: "{{ inventory_hostname }}"
domain: "{{ domain_name }}"
dns_suffix:
- "{{ domain_name }}"
dns_servers: "{{ network_dns }}"
state: poweredon
wait_for_ip_address: yes
delegate_to: localhost
一个示例 var 将是:
additional_disks:
- size_gb: "120"
datastore: "VSAN_Datastore"
错误出来:
fatal: [docker02 -> localhost]: FAILED! => changed=false
module_stderr: |-
Traceback (most recent call last):
File "/home/piwi/.ansible/tmp/ansible-tmp-1586075176.0007603-201082838827202/AnsiballZ_vmware_guest.py", line 102, in <module>
_ansiballz_main()
File "/home/piwi/.ansible/tmp/ansible-tmp-1586075176.0007603-201082838827202/AnsiballZ_vmware_guest.py", line 94, in _ansiballz_main
invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
File "/home/piwi/.ansible/tmp/ansible-tmp-1586075176.0007603-201082838827202/AnsiballZ_vmware_guest.py", line 40, in invoke_module
runpy.run_module(mod_name='ansible.modules.cloud.vmware.vmware_guest', init_globals=None, run_name='__main__', alter_sys=True)
File "/usr/lib/python3.6/runpy.py", line 205, in run_module
return _run_module_code(code, init_globals, run_name, mod_spec)
File "/usr/lib/python3.6/runpy.py", line 96, in _run_module_code
mod_name, mod_spec, pkg_name, script_name)
File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/tmp/ansible_vmware_guest_payload_fiddyn_z/ansible_vmware_guest_payload.zip/ansible/modules/cloud/vmware/vmware_guest.py", line 2834, in <module>
File "/tmp/ansible_vmware_guest_payload_fiddyn_z/ansible_vmware_guest_payload.zip/ansible/modules/cloud/vmware/vmware_guest.py", line 2823, in main
File "/tmp/ansible_vmware_guest_payload_fiddyn_z/ansible_vmware_guest_payload.zip/ansible/modules/cloud/vmware/vmware_guest.py", line 2342, in deploy_vm
File "/tmp/ansible_vmware_guest_payload_fiddyn_z/ansible_vmware_guest_payload.zip/ansible/modules/cloud/vmware/vmware_guest.py", line 2005, in configure_disks
AttributeError: 'str' object has no attribute 'get'
module_stdout: ''
msg: |-
MODULE FAILURE
See stdout/stderr for the exact error
rc: 1
The full traceback is:
Traceback (most recent call last):
File "/home/piwi/.ansible/tmp/ansible-tmp-1586075175.423514-45894902001082/AnsiballZ_vmware_guest.py", line 102, in <module>
_ansiballz_main()
File "/home/piwi/.ansible/tmp/ansible-tmp-1586075175.423514-45894902001082/AnsiballZ_vmware_guest.py", line 94, in _ansiballz_main
invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
File "/home/piwi/.ansible/tmp/ansible-tmp-1586075175.423514-45894902001082/AnsiballZ_vmware_guest.py", line 40, in invoke_module
runpy.run_module(mod_name='ansible.modules.cloud.vmware.vmware_guest', init_globals=None, run_name='__main__', alter_sys=True)
File "/usr/lib/python3.6/runpy.py", line 205, in run_module
return _run_module_code(code, init_globals, run_name, mod_spec)
File "/usr/lib/python3.6/runpy.py", line 96, in _run_module_code
mod_name, mod_spec, pkg_name, script_name)
File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/tmp/ansible_vmware_guest_payload_mspbq6yr/ansible_vmware_guest_payload.zip/ansible/modules/cloud/vmware/vmware_guest.py", line 2834, in <module>
File "/tmp/ansible_vmware_guest_payload_mspbq6yr/ansible_vmware_guest_payload.zip/ansible/modules/cloud/vmware/vmware_guest.py", line 2823, in main
File "/tmp/ansible_vmware_guest_payload_mspbq6yr/ansible_vmware_guest_payload.zip/ansible/modules/cloud/vmware/vmware_guest.py", line 2342, in deploy_vm
File "/tmp/ansible_vmware_guest_payload_mspbq6yr/ansible_vmware_guest_payload.zip/ansible/modules/cloud/vmware/vmware_guest.py", line 2005, in configure_disks
AttributeError: 'str' object has no attribute 'get'
示例变量输出:
ok: [gluster01] =>
vm_disks: |-
[{
'size_kb': 125829120,
'datastore': VSAN_Datastore},
{
'size_gb': 120, 'datastore': VSAN_Datastore},
]
ok: [gluster02] =>
vm_disks: |-
[{
'size_kb': 125829120,
'datastore': VSAN_Datastore},
{
'size_gb': 120, 'datastore': VSAN_Datastore},
]
ok: [docker01] =>
vm_disks: |-
[{
'size_kb': 125829120,
'datastore': VSAN_Datastore},
]
基于以下建议的新代码:但是同样的错误:
- name: "Define new disk structure"
set_fact:
vm_disks: >-
{%- set results = [] -%}
{%- for osdisk in ( template_disk.guest_disk_info | dictsort ) -%}
{%- set od = { "size_kb": osdisk[1].capacity_in_kb } -%}
{%- set _ = od.update({ "datastore": osdisk[1].backing_datastore }) -%}
{%- set _ = results.append(od) -%}
{%- endfor -%}
{%- for disk in additional_disks|default([]) -%}
{%- set d = {"size_gb": disk.size_gb} if (disk.size_gb is defined) else {} -%}
{%- set _ = d.update({"datastore": disk.datastore_name}) -%}
{%- set _ = results.append(d) -%}
{%- endfor -%}
"{{ results }}"
变量调试:
TASK [vm_clone : Debugging var] *******************************************************************************************************************
ok: [gluster01] =>
vm_disks: '"[{''size_kb'': 125829120, ''datastore'': ''VSAN_Datastore''}, {''size_gb'': ''120'', ''datastore'': ''VSAN_Datastore''}]"'
ok: [gluster02] =>
vm_disks: '"[{''size_kb'': 125829120, ''datastore'': ''VSAN_Datastore''}, {''size_gb'': ''120'', ''datastore'': ''VSAN_Datastore''}]"'
ok: [docker01] =>
vm_disks: '"[{''size_kb'': 125829120, ''datastore'': ''VSAN_Datastore''}]"'
ok: [docker02] =>
vm_disks: '"[{''size_kb'': 125829120, ''datastore'': ''VSAN_Datastore''}]"'
ok: [docker03] =>
vm_disks: '"[{''size_kb'': 125829120, ''datastore'': ''VSAN_Datastore''}]"'
ok: [docker04] =>
vm_disks: '"[{''size_kb'': 125829120, ''datastore'': ''VSAN_Datastore''}]"'
ok: [docker05] =>
vm_disks: '"[{''size_kb'': 125829120, ''datastore'': ''VSAN_Datastore''}]"'
这对我来说看起来像一个列表,所以为了验证这一点,我将结果传送到 to_json 过滤器,它错误地说,它不是一个 str 它是一个列表。所以它绝对是一个列表。
解决方案
正如 Uttam 正确指出的那样,但没有提供答案,问题是您set_fact:
产生了一个字符串,但disks
必须是list
of ,正如从精美手册(和代码)dict
中可以看到的那样
有两个原因:第一个是 ansible 仅将看起来 JSON 的字符串自动强制转换为实际的 pythonlist
和dict
结构,但是您使用了带有单引号字符串文字和尾随逗号的python语法,这两者都是非法的 JSON
第二个是永远不应该使用文本在 jinja 中构建丰富的数据结构:它对这些数据结构有强大的支持,以及确保输出是合法的 JSON 和正确转义的字符的美妙| to_json
和过滤器|from_json
但我知道这是很多话,所以我相信你可以做的最小的改变是让你的情况正常工作是停止使用单引号并保护尾随逗号:
- name: "Define new disk structure"
set_fact:
vm_disks: >-
[
{% for disk in (template_disk.guest_disk_info|dictsort) %}
{{ "" if loop.first else "," }}
{
"size_kb": {{ disk[1].capacity_in_kb }},
"datastore": "{{ datastore_name }}"
}
{% endfor %}
{% for disk in additional_disks|default([]) %}
{{ "," if template_disk.guest_disk_info else "" }}
{
{% if disk.size_gb is defined %}"size_gb": {{ disk.size_gb }},{% endif %}
{% if disk.size_mb is defined %}"size_mb": {{ disk.size_mb }},{% endif %}
{% if disk.size_kb is defined %}"size_kb": {{ disk.size_kb }},{% endif %}
"datastore": "{{ datastore_name }}"
}
{% endfor %}
]
正确的代码类似于:
set_fact:
vm_disks: >-
{%- set results = [] -%}
{%- for disk in additional_disks|default([]) -%}
{%- set d = {"datastore": datastore_name} -%}
{%- set _ = d.update({"size_gb": disk.size_gb} if (disk.size_gb is defined) else {}) -%}
{%- set _ = results.append(d) -%}
{%- endfor -%}
{{ results }}
推荐阅读
- python - Python pandas 计算多列日期时间的平均值
- powershell - 如何远程运行 New-DfsnRoot
- c# - 如何使用外键访问关系表上不同变量的值
- angular - 视频自动播放器离子
- excel - 为什么在包含 Double 的行上执行 VBA Step Over 会导致溢出错误?
- c# - 指定 ON DELETE NO ACTION 为同一个表创建 3 个外键 - 实体框架
- typescript - 返回类型为非空且基于输入类型
- c# - 限制框架在 c# NetCore 中的 SOAP xml 消息中添加可选标签
- python - 在 TensorFlow Functional API 中保存和加载具有相同图形的多个模型
- android - 在 Xamarin 表单中测量字符串大小 (Android)