【问题标题】:Loop isn't iterating over last line of list循环没有迭代列表的最后一行
【发布时间】:2020-05-29 12:04:13
【问题描述】:

我在剧本中创建了一个模板,我想用哈希列表对其进行迭代。我想将这个输出添加到另一个 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"
        }
    ]
}

【问题讨论】:

    标签: loops ansible


    【解决方案1】:

    您的问题是由于通过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
    

    【讨论】:

    • 我更喜欢最初的答案,我已经将它转移到我的环境中,但有一件事仍然阻止了我。 'desc': "[{}]".format(item.port) 给了我一个“带有 base10 的 int() 的内部文字”错误。但在我的测试中它工作正常!很好的答案
    • 我想通了,它必须将该对象作为列表传递,但模板作为字符串“[9091]”传递。改为使用port: [item.port],因为我在该字段中只有一个端口。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-20
    • 2019-08-16
    • 2013-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多