【问题标题】:How to cast left side value in Ansible/Jinja selectattr filter condition如何在 Ansible/Jinja selectattr 过滤条件中投射左侧值
【发布时间】:2020-09-08 00:02:49
【问题描述】:

我在 Python 3.8.4 上使用 Ansible 2.9.13

在 Ansible 剧本中,我有一个字典列表,我想只循环 pct_used 值大于 90 的项目子集。

这是一个说明它的剧本:

---
- hosts: localhost
  gather_facts: False
  vars:
    usage_records: [{mount: "/abc", pct_used: "50"}, {mount: "/def", pct_used: "75"}, {mount: "/ghi", pct_used: "95"}]
  tasks:
    - name: process just records with more than 90 percent usage
      debug:
        msg: "{{ item }}"
      loop: "{{ usage_records|selectattr('pct_used','ge', 90)|list }}"

但如果我运行它,它会抱怨 pct_used 值是 AnsibleUnicode 字符串而不是整数:

TASK [process just records with more than 90 percent usage] ************************************************************
fatal: [localhost]: FAILED! => {"msg": "Unexpected templating type error occurred on ({{ usage_records|selectattr('pct_used','ge', 90)|list }}): '>=' not supported between instances of 'AnsibleUnicode' and 'int'"}

现在,我意识到问题在于字典中的 pct_used 值是字符串而不是 int。如果我重写了usage_records 定义行,而数字周围没有引号:

    usage_records: [{mount: "/abc", pct_used: 50}, {mount: "/def", pct_used: 75}, {mount: "/ghi", pct_used: 95}]

它确实按预期工作:

ok: [localhost] => (item={'mount': '/ghi', 'pct_used': 95}) => {
    "msg": {
        "mount": "/ghi",
        "pct_used": 95
    }
}

问题是,在我的实际用例中,数据是从文件中读取的,默认都是字符串,所以我无法从源头更改。

我觉得必须有一种方法可以在循环中的过滤器表达式中进行强制转换,但我就是想不通。

如果这不可能,那么更新列表本身以将值转换为整数的最佳方法是什么?

【问题讨论】:

    标签: ansible jinja2


    【解决方案1】:

    好吧,这里有一个选项:

    ---
    - hosts: localhost
      gather_facts: False
      vars:
        usage_records:
          - mount: "/abc"
            pct_used: "50"
          - mount: "/def"
            pct_used: "75"
          - mount: "/ghi"
            pct_used: "95"
      tasks:
        - name: process just records with more than 90 percent usage
          debug:
            msg: "{{ item }}"
          loop: >-
            {{ usage_records|zip(
                usage_records|
                map(attribute='pct_used')|
                map('int')
              )|
              selectattr('1', 'ge', 90)|list
            }}
    

    这会导致:

    TASK [process just records with more than 90 percent usage] ***********************************
    ok: [localhost] => (item=[{'mount': '/ghi', 'pct_used': '95'}, 95]) => {
        "msg": [
            {
                "mount": "/ghi",
                "pct_used": "95"
            },
            95
        ]
    }
    
    

    在这里,我们使用 map 的调用来 (a) 创建仅包含 pct_used 值的第二个列表,然后 (b) 将这些全部传递给 int 过滤器。最后,我们zip 将原始列表与我们的新列表放在一起。这给了我们一个最终列表,其中每个项目由第一个列表中的字典和第二个列表中的整数值组成(我们可以使用它进行过滤)。

    【讨论】:

    • 非常感谢您的解决方案,它确实有效,但它比我希望的要复杂一些。我可以想象,在某些情况下,某些人可能需要这样的东西,所以在这里作为参考是很好的。我只是觉得我找到了一些更清洁的东西。回想起来,我认为我的问题想多了。
    【解决方案2】:

    我想我找到了一个更清洁的解决方案。显然,您可以同时使用循环和 when 子句,并且 when 子句可以引用循环变量。 when 子句在逻辑上提供了更多的灵活性。

    ---
    - hosts: localhost
      gather_facts: False
      vars:
        usage_records: [{mount: "/abc", pct_used: 50}, {mount: "/def", pct_used: 75}, {mount: "/ghi", pct_used: 95}]
      tasks:
        - name: process just records with more than 90 percent usage
          debug:
            msg: "{{ item }}"
          when: item.pct_used|int >= 90
          loop: "{{ usage_records }}"
    

    仍然得到想要的输出:

    ok: [localhost] => (item={'mount': '/ghi', 'pct_used': 95}) => {
        "msg": {
            "mount": "/ghi",
            "pct_used": 95
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-24
      • 2022-01-22
      • 2013-01-19
      • 2014-12-11
      • 1970-01-01
      相关资源
      最近更新 更多