【问题标题】:Ansible - how to use selectattr with yaml of different keysAnsible - 如何将 selectattr 与不同键的 yaml 一起使用
【发布时间】:2019-09-09 17:05:44
【问题描述】:

我正在努力做一件简单的事情(我认为这应该很容易),通过解析 yaml 并过滤 Ansible 中的某些键。

我的 yaml 文件如下所示:

---

- vm: "vm1"
  ip: 10.10.10.1
- vm: "vm2"
  ip: 10.10.10.2
- test_vm: something
- another_vm: something_other

所以我认为不是像

这样的表达
lookup('file','my_file.yaml') | from_yaml | selectattr('vm','search','vm1')|list

可以,但会出现类似

的错误
fatal: [localhost]: FAILED! => {"msg": "Unexpected templating type error occurred on ({{ lookup('file','{{sysfile}}') | from_yaml | selectattr('vm','search','vm1')|list}}): expected string or bytes-like object"}

如果我删除 test_vm 和 another_vm 键,它工作正常。

ok: [localhost] => {
    "msg": [
        {
            "ip": "10.10.10.1",
            "vm": "vm1"
        }
    ]
}

如果我尝试搜索 test_vm 键,它会失败:

fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'test_vm'\n\nThe error appears to be ...

selectattr 过滤器是否期望列表中的所有 dicts 具有相同的键?因为不能使用 Jinja2 过滤自定义 dicts 列表没有任何意义。

例如,如果我有一个更复杂的 yaml(不是那么平坦),我是否仅限于在 Ansible 中搜索和过滤?

例如,如果我有一个 yaml,如下所示:

---

- vm: "vm1"
  ip: 10.10.10.1
- vm: "vm2"
  ip: 10.10.10.2
- test_vm: something
   - process_1: X
   - process_2: Y
   - process_3: Z
- another_vm: something_other

例如,我如何快速过滤 process_2? 有什么办法吗?

非常感谢。

【问题讨论】:

    标签: python ansible jinja2


    【解决方案1】:

    更准确地说,它期望列表中的所有 dicts 都具有您选择的属性。

    并非所有过滤器功能都 100% 正确,通过并非由所有元素定义的属性来选择对象:

    {{ test_var | selectattr('vm','defined') |selectattr('vm','equalto','vm1') | list }} 
    

    【讨论】:

    • 你是对的。这是很久以前写的,当时我基本上是在 SO(和 ansible)上开始的。我调整了我的答案。谢谢。
    【解决方案2】:

    selectattr 过滤器是否期望列表中的所有 dicts 具有相同的键?

    更准确地说,它期望列表中的所有 dicts 都具有您选择的属性。如果不是列表中的所有 dict 都有它,您必须首先过滤掉未定义的项目。这也可以通过selectattr 完成。 (感谢@Randy 在我最初的回答之后让这一点更清楚)。

    在您的情况下,json_query filter(实现jmespath)有时也可以以更紧凑的方式完成这项工作。但它不是核心过滤器,需要安装community.general collection

    以下是从您的上述要求中提取的一些示例,这些示例同时使用核心过滤器和json_query 解决方案解决。

    剧本:

    ---
    - name: "Filter data with core filters or json query"
      hosts: "localhost"
      gather_facts: false
    
      vars:
        # Your initial data on a single line for legibility
        test_var: [{"vm":"vm1"},{"ip":"10.10.10.1"},{"vm":"vm2","ip":"10.10.10.2"},{"test_vm":"something","process_1":"X","process_2":"Y","process_3":"Z"},{"another_vm":"something_other"}]
    
      tasks:
        - name: Get objects having vm==vm1
          vars:
            msg: |-
              With core filters: {{ test_var | selectattr('vm', 'defined') | selectattr('vm', '==', 'vm1') | list }}
              With json_query: {{ test_var | json_query("[?vm=='vm1']") | list }}
          debug:
            msg: "{{ msg.split('\n') }}"
    
        - name: Get all objects having vm attribute
          vars:
            msg: |-
              With core filters: {{ test_var | selectattr('vm', 'defined') | list }}
              With json_query: {{ test_var | json_query("[?vm]") | list }}
          debug:
            msg: "{{ msg.split('\n') }}"
    
        - name: Get all objects having process_2 attribute
          vars:
            msg: |-
              With core filters: {{ test_var | selectattr('process_2', 'defined') | list }}
              With json_query: {{ test_var | json_query("[?process_2]") | list }}
          debug:
            msg: "{{ msg.split('\n') }}"
    
        - name: Get only a list of process_2 attributes
          vars:
            msg: |-
              With core filters: {{ test_var | selectattr('process_2', 'defined') | map(attribute='process_2') | list }}
              With json_query: {{ test_var | json_query("[].process_2") | list }}
          debug:
            msg: "{{ msg.split('\n') }}"
    

    给出:

    PLAY [Filter data with core filters or json query] *********************************************************************
    
    TASK [Get objects having vm==vm1] *********************************************************************
    ok: [localhost] => {
        "msg": [
            "With core filters: [{'vm': 'vm1', 'ip': '10.10.10.1'}]",
            "With json_query: [{'vm': 'vm1', 'ip': '10.10.10.1'}]"
        ]
    }
    
    TASK [Get all objects having vm attribute] *********************************************************************
    ok: [localhost] => {
        "msg": [
            "With core filters: [{'vm': 'vm1', 'ip': '10.10.10.1'}, {'vm': 'vm2', 'ip': '10.10.10.2'}]",
            "With json_query: [{'vm': 'vm1', 'ip': '10.10.10.1'}, {'vm': 'vm2', 'ip': '10.10.10.2'}]"
        ]
    }
    
    TASK [Get all objects having process_2 attribute] *********************************************************************
    ok: [localhost] => {
        "msg": [
            "With core filters: [{'test_vm': 'something', 'process_1': 'X', 'process_2': 'Y', 'process_3': 'Z'}]",
            "With json_query: [{'test_vm': 'something', 'process_1': 'X', 'process_2': 'Y', 'process_3': 'Z'}]"
        ]
    }
    
    TASK [Get only a list of process_2 attributes] *********************************************************************
    ok: [localhost] => {
        "msg": [
            "With core filters: ['Y']",
            "With json_query: ['Y']"
        ]
    }
    
    PLAY RECAP *********************************************************************
    localhost                  : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    

    【讨论】:

    • 谢谢@Zeitounator。我已经看过 json_query 并解决了我的问题。但我很高兴你回应了。很好的例子。坏事是 jmespath 中的过滤器不是很解释。例如,使用逻辑或字典进行过滤时,我遇到了麻烦。无论如何,最后我找到了它,使用包含但仍然... Jinja2 和 jmespath 可能会更好。
    • 谢谢。帮助使用 json_query 过滤键值对。
    • @jkalivas。我遇到了这个现在很旧的答案。随着时间和经验(并考虑到 Randy 的回答),我认为是时候修改我的帖子了。使用现有的 json_query 解决方案进行了更新,但也使用了核心过滤器。
    猜你喜欢
    • 2022-01-22
    • 2021-10-08
    • 2017-12-22
    • 2016-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-24
    • 1970-01-01
    相关资源
    最近更新 更多