Ansible:按其属性过滤列表

111

我在Ansible中注册了一个名为“network”的变量:

    {
        "addresses": {
            "private_ext": [
                {
                    "type": "fixed",
                    "addr": "172.16.2.100"
                }
            ],
            "private_man": [
                {
                    "type": "fixed",
                    "addr": "172.16.1.100"
                },
                {
                    "type": "floating",
                    "addr": "10.90.80.10"
                }
            ]
        }
    }

像这样做,是否有可能获取类型为“floating”的IP地址(“addr”)?

- debug: var={{ network.addresses.private_man | filter type="fixed" | get "addr" }}

我知道语法是错误的,但你可以理解我的意思。

3个回答

161

如果要过滤字典列表,可以使用selectattr filterequalto test

network.addresses.private_man | selectattr("type", "equalto", "fixed")

以上需要Jinja2 v2.8或更高版本(无论Ansible版本如何)。


Ansible 还 拥有测试matchsearch, 可以使用正则表达式:

match 要求在字符串中进行完全匹配,而 search 要求在字符串内部进行匹配。

network.addresses.private_man | selectattr("type", "match", "^fixed$")

为了将字典列表转换为字符串列表,只获取addr字段的列表,您可以使用map filter
... | map(attribute='addr') | list

或者如果你想要一个逗号分隔的字符串:

... | map(attribute='addr') | join(',')

合并后,它会看起来像这样。

- debug: msg={{ network.addresses.private_man | selectattr("type", "equalto", "fixed") | map(attribute='addr') | join(',') }}

1
如果你想要它们作为一个列表而不是逗号分隔的字符串,你也可以使用:{{ network.addresses.private_man | selectattr("type", "equalto", "fixed") | map(attribute='addr') | list }} - TrinitronX
1
你提供的链接改变了吗?我在那个页面上找不到“match”和“search”。 - activedecay
1
@activedecay 看起来那个页面上已经没有了,是的。现在我能找到的唯一提及它的地方是在这个页面上:https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html#testing-strings 我更新了链接,谢谢! - udondan

45

我已经提交了一个拉取请求(可在Ansible 2.2+中使用),它将通过在Ansible上添加jmespath查询支持,使这些情况更容易处理。对于您的情况,它将起到以下作用:

- debug: msg="{{ addresses | json_query(\"private_man[?type=='fixed'].addr\") }}"

会返回:

ok: [localhost] => {
    "msg": [
        "172.16.1.100"
    ]
}

4
在运行json_query筛选器之前,您需要安装“ jmespath”。 - ceving
我放弃尝试安装jmespath了,因为Python版本不同。这不是像Ansible 2.4+上看起来那么简单的解决方案。 - Stuart Watt
1
@StuartWatt 你可能需要使用pyenv来抽象出操作系统Python版本。你可以尝试使用pyenv、pyenv virtualenvs,或者有些人更喜欢pipenv。它们都能够正常工作。 - Jepper
@Jepper 如果你需要一个全新的Python来使用Ansible,那就是一场失败的战斗。(我之前也用过pyenv,但仍然遇到了这些问题 :-)) - Stuart Watt
有Python相关问题的人:根据正在运行的操作系统,这可能需要一些小技巧来帮助ansible使用正确的Python版本。我遇到过Python 2和3都安装的问题,而Ansible倾向于使用v2。您可能需要运行以下命令(例如,如果目标主机是Debian): sudo update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1 sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 2 sudo update-alternatives --set python /usr/bin/python3(假设已安装Python2.7和Python3) - acat

29

不一定更好,但既然有选择的机会,就来看看如何使用Jinja语句

- debug:
    msg: "{% for address in network.addresses.private_man %}\
        {% if address.type == 'fixed' %}\
          {{ address.addr }}\
        {% endif %}\
      {% endfor %}"

或者如果你更喜欢将所有内容放在一行中:

- debug:
    msg: "{% for address in network.addresses.private_man if address.type == 'fixed' %}{{ address.addr }}{% endfor %}"

返回结果如下:

ok: [localhost] => {
    "msg": "172.16.1.100"
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接