Return 来自 set_fact 的与 var 相同的数据

Return same data from set_fact as var

我正在尝试创建字典。使用 var 时一切正常。但是 Set_fact 好像有什么东西被忽略了,我无法隔离。

正在阅读 Jinja2 手册,多次迭代

---
- hosts: localbox   gather_facts: false

  vars:
      app_servers: 5
      ipaddress_base: "192.168.0"
      rmi_portbase: 10000
      host_info: |
        {% set res = [] -%}
        {%- for number in range(1,app_servers + 1) -%}
          {% set ignored = res.extend([{
            'hostname': 'app' + number|string,
            'ipaddress': ipaddress_base + '.' + number|string,
            'rmi_port': rmi_portbase|int + ( number * 10)
            }]) -%}
        {%- endfor %}
        {{ res }}

  tasks:

    - name: thing
      set_fact: 
        thing2: "{% set res = [] -%}
        {%- for number in range(1,app_servers + 1) -%}
          {% set ignored = res.extend([{
            'hostname': 'app' + number|string,
            'ipaddress': ipaddress_baase + '.' + number|string,
            'rmi_port': rmi_portbase|int + ( number * 10)
            }]) -%}
        {%- endfor %}
        {{ res }}"

    - debug: var=host_info[0].hostname
    - debug: var=thing2[0]

我希望从 thing2 得到像 host_info 这样的结果。

TASK [debug] *******************************************************************
ok: [localhost] => {
    "host_info[0].hostname": "app1"
}

TASK [debug] *******************************************************************
ok: [localhost] => {
    "thing2[0]": " "
}

如果您 运行 您发布的剧本,它应该会失败并出现以下错误:

TASK [thing]


fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ipaddress_baase' is undefined\n\nThe error appears to have been in '/home/lars/ tmp/ansible/playbook.yml': line 25, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: thing\n ^ her e\n"}

事实上,您将 ipaddress_base 拼错为 ipaddress_baase(注意多余的 a)。如果您修复该拼写错误并添加修改最终的 debug 任务,如下所示:

- debug: var=thing2

您应该看到以下输出:

TASK [debug] **********************************************************************************************************************************************************************************
ok: [localhost] => {
    "thing2": " [{'rmi_port': 10010, 'hostname': u'app1', 'ipaddress': u'192.168.0.1'}, {'rmi_port': 10020, 'hostname': u'app2', 'ipaddress': u'192.168.0.2'}, {'rmi_port': 10030, 'hostname': u'app3', 'ipaddress': u'192.168.0.3'}, {'rmi_port': 10040, 'hostname': u'app4', 'ipaddress': u'192.168.0.4'}, {'rmi_port': 10050, 'hostname': u'app5', 'ipaddress': u'192.168.0.5'}]"
}

向我们展示的是 thing2 是一个字符串,而不是字典。因此,当您为 thing2[0] 分配任务时,您将获得该字符串的 0 位置的字符。

原因它是一个字符串而不是字典是因为索引0处的前导space。我们可以通过将 endfor 语句中的终端标记从 %} 更改为 -%} 来修复它,这将吃掉任何以下 whitespace:

- set_fact:
    thing2: "{% set res = [] -%}
    {%- for number in range(1,app_servers + 1) -%}
      {% set ignored = res.extend([{
        'hostname': 'app' + number|string,
        'ipaddress': ipaddress_baase + '.' + number|string,
        'rmi_port': rmi_portbase|int + ( number * 10)
        }]) -%}
    {%- endfor -%}
    {{ res }}"

有了这个和你原来的 debug 任务,我们看到输出:

TASK [set_fact] *******************************************************************************************************************************************************************************
ok: [localhost]

TASK [debug] **********************************************************************************************************************************************************************************
ok: [localhost] => {
    "host_info[0].hostname": "app1"
}

TASK [debug] **********************************************************************************************************************************************************************************
ok: [localhost] => {
    "thing2[0]": {
        "hostname": "app1", 
        "ipaddress": "192.168.0.1", 
        "rmi_port": 10010
    }
}

话虽如此,我会停止尝试使用这种技术来生成复杂的数据结构,因为正如我们所见,它很容易出错。我会这样写:

- set_fact: 
    thing2: "{{ thing2|default([]) + [{
      'hostname': 'app' ~ item,
      'ipaddress': ipaddress_base ~ '.' ~ item,
      'rmi_port': rmi_portbase ~ (item * 10)}]
      }}"
  loop: "{{ range(1, app_servers+1)|list }}"

我认为这既易于实现又易于阅读。