Ansible: фильтровать список по его атрибутам

84

У меня есть переменная с именем "сеть", зарегистрированная в Ansible:

{
    "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"
            }
        ]
    }
}

Возможно ли получить IP-адрес ("addr") с помощью type = "Floating", сделав что-то подобное?

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

Я знаю, что синтаксис неправильный, но идею вы поняли.

Гвидо
источник

Ответы:

41

Я отправил запрос на перенос (доступен в Ansible 2.2+), который упростит подобные ситуации, добавив поддержку запросов jmespath в Ansible. В вашем случае это будет работать так:

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

вернется:

ok: [localhost] => {
    "msg": [
        "172.16.1.100"
    ]
}
Филипе
источник
3
Перед запуском фильтра json_query необходимо установить jmespath.
ceven
И я просто отказался от попыток установить jmespath из-за различий в версиях Python. Это не такое тривиальное решение, как кажется на Ansible 2.4+
Стюарт Ватт
1
@StuartWatt вам может понадобиться pyenv, чтобы абстрагироваться от версии Python для операционной системы. Вы можете попробовать pyenv, pyenv virtualenvs, или некоторые люди предпочитают pipenv. Они все равно будут работать.
Jepper 07
@Jepper Если вам нужен совершенно новый Python для использования Ansible, это проигрышная битва. (И я использовал pyenv, и эти проблемы все еще были :-)
Стюарт Ватт
128

Чтобы отфильтровать список диктовок, вы можете использовать фильтр selectattr вместе с тестом equalto :

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

Для вышеуказанного требуется Jinja2 v2.8 или новее (независимо от версии Ansible).


В Ansible также есть тесты matchиsearch , которые принимают регулярные выражения:

matchпотребует полного совпадения в строке, в то время как searchпотребуется совпадение внутри строки.

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

Чтобы сократить список диктовок до списка строк, чтобы вы получали только список addrполей, вы можете использовать фильтр карты :

... | 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
изменилась ли предоставленная вами ссылка? я не могу найти "совпадение" и "поиск" на этой странице
activedecay
1
@activedecay Похоже, он исчез с той страницы, да. Единственное упоминание, которое я смог найти сейчас, находится на этой странице: docs.ansible.com/ansible/latest/user_guide/… Я обновил ссылку, спасибо!
удондан
19

Не обязательно лучше, но, поскольку приятно иметь варианты, вот как это сделать с помощью операторов 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"
}
bmaupin
источник