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
Вам потрібно встановити "jmespath" перед запуском фільтра json_query.
закінчення

І я просто відмовився від спроби встановити jmespath через різницю у версії Python. Це не таке тривіальне рішення, як виглядає на Ansible 2.4+
Стюарт Ватт,

1
@StuartWatt вам може знадобитися pyenv, щоб абстрагуватися від версії python операційної системи. Ви можете спробувати pyenv, pyenv virtualenvs, або деякі люди віддають перевагу pipenv. Вони будуть працювати все одно.
Джеппер,

@Jepper Якщо вам потрібен абсолютно новий Python, щоб використовувати Ansible, це програш. (А я використовував pyenv і все ще мав ці проблеми :-)
Стюарт Ватт

128

Щоб відфільтрувати список диктовок, ви можете скористатися фільтром selectattr разом із тестом дорівнює :

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/... Я оновив посилання, дякую!
udondan

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.