Ansible内联jinja模板化为列表

Ansible内联jinja模板化为列表,ansible,jinja2,Ansible,Jinja2,我不明白为什么这行不通。 我正在使用jinja动态生成我将传递给vmware_来宾模块的列表,以便我可以在主机或组变量中决定是否要添加其他磁盘。 我正在使用vmware_disk_info模块查找模板磁盘的大小,然后添加我将在组变量中定义的任何其他磁盘。 在我看来,输出的列表不是一个列表 --- - name: "Get facts for named template" vmware_guest_disk_info: hostname: "{{ vcenter_server }}"

我不明白为什么这行不通。 我正在使用jinja动态生成我将传递给vmware_来宾模块的列表,以便我可以在主机或组变量中决定是否要添加其他磁盘。 我正在使用vmware_disk_info模块查找模板磁盘的大小,然后添加我将在组变量中定义的任何其他磁盘。 在我看来,输出的列表不是一个列表

---
- 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
一个例子是:

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'
基于以下建议的新代码: 但是,同样的错误是:

- 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 }}"
var调试:

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正确指出的,但没有提供答案,问题是您的
设置事实:
生成字符串,但
磁盘必须是
目录的
列表,从(和)可以看出

有两个原因:第一个原因是ansible仅自动将JSON字符串强制转换为实际的python
list
dict
结构,但您使用了带有单引号字符串文本和尾随逗号的python语法,这两种语法都是非法的JSON

第二,绝不能使用文本在jinja中构建丰富的数据结构:它强烈支持这些数据结构,以及来自json的精彩和
|过滤器,以确保输出是合法的json和正确的转义字符

但是我知道这是一大堆单词,所以我相信你能做的最小的改变就是停止使用单引号并保护后面的逗号:

-name:“定义新磁盘结构”
设定事实:
虚拟机磁盘:>-
[
{中磁盘的%(模板_disk.guest _disk |信息| dictsort)%}
{{“if loop.first else”,“}}
{
“大小\ kb”:{{磁盘[1]。容量\单位为\ kb},
“数据存储”:“{datastore_name}”
}
{%endfor%}
{对于附加|u磁盘中的磁盘,{默认值([])%}
{{”,“如果模板\u disk.guest\u disk\u info else”“}
{
{%if disk.size_gb定义为%}“size_gb”:{{disk.size_gb},{%endif%}
{%if disk.size_-mb定义为%}“size_-mb”:{{disk.size_-mb},{%endif%}
{%if disk.size_-kb定义为%}“size_-kb”:{{disk.size_-kb},{%endif%}
“数据存储”:“{datastore_name}”
}
{%endfor%}
]
正确的代码类似于:

set\u事实:
虚拟机磁盘:>-
{%-设置结果=[]-%}
{%-对于附加磁盘中的磁盘|默认值([])-%}
{%-集合d={“数据存储”:数据存储{u name}-%}
{%-set}=d.update({“size\u-gb”:disk.size\u-gb}如果(disk.size\u-gb已定义)其他{})-%}
{%-set}=results.append(d)-%}
{%-endfor-%}
{{results}}

正如Uttam正确指出的,但没有提供答案,问题是您的
设置事实:
生成字符串,但
磁盘必须是
目录的
列表

有两个原因:第一个原因是ansible仅自动将JSON字符串强制转换为实际的python
list
dict
结构,但您使用了带有单引号字符串文本和尾随逗号的python语法,这两种语法都是非法的JSON

第二,绝不能使用文本在jinja中构建丰富的数据结构:它强烈支持这些数据结构,以及来自json的精彩和
|过滤器,以确保输出是合法的json和正确的转义字符

但是我知道这是一大堆单词,所以我相信你能做的最小的改变就是停止使用单引号并保护后面的逗号:

-name:“定义新磁盘结构”
设定事实:
虚拟机磁盘:>-
[
{中磁盘的%(模板_disk.guest _disk |信息| dictsort)%}
{{“if loop.first else”,“}}
{
“大小\ kb”:{{磁盘[1]。容量\单位为\ kb},
“数据存储”:“{datastore_name}”
}
{%endfor%}
{对于附加|u磁盘中的磁盘,{默认值([])%}
{{”,“如果模板\u disk.guest\u disk\u info else”“}
{
{%if disk.size_gb定义为%}“size_gb”:{{disk.size_gb},{%endif%}
{%if disk.size_-mb定义为%}“size_-mb”:{{disk.size_-mb},{%endif%}
{%if disk.size_-kb定义为%}“size_-kb”:{{disk.size_-kb},{%endif%}
“数据存储”:“{datastore_name}”
}
{%endfor%}
]
正确的代码类似于:

set\u事实:
虚拟机磁盘:>-
{%-设置结果=[]-%}
{%-对于附加磁盘中的磁盘|默认值([])-%}
{%-集合d={“数据存储”:数据存储{u name}-%}
{%-set}=d.update({“size\u-gb”:disk.size\u-gb}如果(disk.size\u-gb已定义)其他{})-%}
{%-set}=results.append(d)-%}
{%-endfor-%}
{{results}}

最终工作代码,非常感谢@mdaniel

- 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: >-
      {%- 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([]) -%}
        {%- if (disk.size_gb is defined) -%}
          {%- set d = {"size_gb": disk.size_gb} -%}
          {%- set _ = d.update({"datastore": disk.datastore_name}) -%}
          {%- set _ = results.append(d) -%}
        {%- endif -%}
        {%- if (disk.size_mb is defined) -%}
          {%- set d = {"size_mb": disk.size_mb} -%}
          {%- set _ = d.update({"datastore": disk.datastore_name}) -%}
          {%- set _ = results.append(d) -%}
        {%- endif -%}
        {%- if (disk.size_kb is defined) -%}
          {%- set d = {"size_kb": disk.size_kb} -%}
          {%- set _ = d.update({"datastore": disk.datastore_name}) -%}
          {%- set _ = results.append(d) -%}
        {%- endif -%}
      {%- endfor -%}
      {{ results }}

- 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_name: "VSAN_Datastore"
  - size_mb: "10240"
    datastore_name: "VSAN_Datastore"
  - size_kb: "10240000"
    datastore_name: "VSAN_Datastore"

最终工作代码,非常感谢@mdaniel

- 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: >-
      {%- 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([]) -%}
        {%- if (disk.size_gb is defined) -%}
          {%- set d = {"size_gb": disk.size_gb} -%}
          {%- set _ = d.update({"datastore": disk.datastore_name}) -%}
          {%- set _ = results.append(d) -%}
        {%- endif -%}
        {%- if (disk.size_mb is defined) -%}
          {%- set d = {"size_mb": disk.size_mb} -%}
          {%- set _ = d.update({"datastore": disk.datastore_name}) -%}
          {%- set _ = results.append(d) -%}
        {%- endif -%}
        {%- if (disk.size_kb is defined) -%}
          {%- set d = {"size_kb": disk.size_kb} -%}
          {%- set _ = d.update({"datastore": disk.datastore_name}) -%}
          {%- set _ = results.append(d) -%}
        {%- endif -%}
      {%- endfor -%}
      {{ results }}

- 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_name: "VSAN_Datastore"
  - size_mb: "10240"
    datastore_name: "VSAN_Datastore"
  - size_kb: "10240000"
    datastore_name: "VSAN_Datastore"

Add“-name:打印变量\n debug:\n var:additional_disks”在set_fact任务之后,验证变量是否定义正确。看一看,我添加了变量的输出您看到这个符号了吗:|-不知道这是从哪里来的好吧,这可能是一个愚蠢的解决方法,但是您是否尝试使用{{vm_disks | replace(“|-”,“”)}?等等,多行“>”就足够了吗?为什么在“>-”中使用连字符?另一个愚蠢的(也许不是)解决方法是将所有的Jinja代码