首页 > 解决方案 > 循环没有迭代列表的最后一行

问题描述

我在剧本中创建了一个模板,我想用哈希列表进行迭代。我想将此输出添加到另一个 var 以在以下模块中使用。

模板有效,循环看起来也有效,但它从不添加列表中的最后一项。我在测试游戏中重新创建了它。

---
- hosts: localhost
  tasks:
  - name: Init
    set_fact:
      foo: []
      fqdn: "test.com"
      template: []

  - name: portlist
    set_fact:
      portlist:
      - { port: 9091, index: 1 }
      - { port: 9092, index: 2 }
      - { port: 9093, index: 3 }
      - { port: 9094, index: 4 }

  - name: generate policy
    set_fact:
      template:
      - name: "traffic to {{ item.port }}"
        index: "{{ item.index }}"
        match:
          desc: "[{{ item.port }}]" # The field needs to be passed as a list
          name: "{{ fqdn }}_{{ item.port }}"
          port: "{{ item.port }}"
      foo: "{{ foo + template }}"
    loop: "{{ portlist }}"

  - debug:
      var: foo

我知道我可以使用默认值而不是初始化 vars 来使这个游戏更小,但这感觉更容易阅读以进行故障排除。

该播放会产生一个哈希列表,然后我可以将其输入到策略模块中。然而,它只给了我列表中的 3 个项目,并且错过了端口列表中的最后一个项目。

TASK [debug] ***************************************************
ok: [localhost] => {
    "foo": [
        {
            "action": {
                "ref": "test.com_9091",
                "this": true
            },
            "enable": true,
            "index": "1",
            "match": {
                "port": {
                    "criteria": "IS_IN",
                    "port": [
                        9091
                    ]
                }
            },
            "name": "traffic to 9091"
        },
        {
            "action": {
                "ref": "test.com_9092",
                "this": true
            },
            "enable": true,
            "index": "2",
            "match": {
                "port": {
                    "criteria": "IS_IN",
                    "port": [
                        9092
                    ]
                }
            },
            "name": "traffic to 9092"
        },
        {
            "action": {
                "ref": "test.com_9093",
                "this": true
            },
            "enable": true,
            "index": "3",
            "match": {
                "port": {
                    "criteria": "IS_IN",
                    "port": [
                        9093
                    ]
                }
            },
            "name": "traffic to 9093"
        }
    ]
}

标签: loopsansible

解决方案


您的问题是由于在任务完成set_fact之前定义的变量不可用。set_fact这意味着当您设置:

foo: "{{ foo + template }}"

template您可以从上一个循环迭代中看到 的值。

处理此问题的一种方法是重写您的set_fact任务以foo直接设置:

---
- hosts: localhost
  gather_facts: false
  vars:
    fqdn: "test.com"
    portlist:
      - {port: 9091, index: 1}
      - {port: 9092, index: 2}
      - {port: 9093, index: 3}
      - {port: 9094, index: 4}
  tasks:
    - name: generate policy
      set_fact:
        foo: >-
          {{
            foo + [{
              'name': 'traffic to {}'.format(item.port),
              'index': item.index,
              'match': {
                'desc': "[{}]".format(item.port),
                'name': '{}_{}'.format(fqdn, item.port),
                'port': item.port
              }
            }]
          }}
      vars:
        foo: []
      loop: "{{ portlist }}"

    - debug:
        var: foo

这将输出:

TASK [debug] *********************************************************************************************************************************************************************************
ok: [localhost] => {
    "foo": [
        {
            "index": 1,
            "match": {
                "desc": "[9091]",
                "name": "test.com_9091",
                "port": 9091
            },
            "name": "traffic to 9091"
        },
        {
            "index": 2,
            "match": {
                "desc": "[9092]",
                "name": "test.com_9092",
                "port": 9092
            },
            "name": "traffic to 9092"
        },
        {
            "index": 3,
            "match": {
                "desc": "[9093]",
                "name": "test.com_9093",
                "port": 9093
            },
            "name": "traffic to 9093"
        },
        {
            "index": 4,
            "match": {
                "desc": "[9094]",
                "name": "test.com_9094",
                "port": 9094
            },
            "name": "traffic to 9094"
        }
    ]
}

如果您发现基于模板的解决方案更具可读性,您可以使用以下两个set_fact任务重写它:

---
- hosts: localhost
  gather_facts: false
  vars:
    fqdn: "test.com"
    portlist:
      - {port: 9091, index: 1}
      - {port: 9092, index: 2}
      - {port: 9093, index: 3}
      - {port: 9094, index: 4}
  tasks:
  - name: generate policy
    set_fact:
      template:
        name: "traffic to {{ item.port }}"
        index: "{{ item.index }}"
        match:
          desc: "[{{ item.port }}]" # The field needs to be passed as a list
          name: "{{ fqdn }}_{{ item.port }}"
          port: "{{ item.port }}"
    loop: "{{ portlist }}"
    register: foo

  - set_fact:
      foo: "{{ foo.results | map(attribute='ansible_facts.template') | list }}"

  - debug:
      var: foo

推荐阅读