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"
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.