Я ищу лучшее решение для фильтрации списка диктов по ключу A и возврата списка значений ключа B. Чтобы быть более конкретным - у каждого хоста есть dict:
infrastructure:
name: "x..."
network:
address: "1..."
Есть хосты, где network.address
определено, и есть хосты, где network.address
не определено. Теперь мне нужен список всех infrastructure.name
с определенными network.address
.
- name: "Define Alias fact"
set_fact:
alias: []
- name: "Add Aliases for all hosts with network.address is defined"
set_fact:
alias: "{{ alias + [hostvars[host].infrastructure.name + '-alias'] }}"
when:
- "hostvars[host].network is defined"
- "hostvars[host].network.address is defined"
with_items: "{{ groups['all'] }}"
loop_control:
loop_var: host
Это работает, но немного запутанно, потому что я много раз вызываю set_fact и добавляю элементы в список.
Когда я смотрю на:
- name: "Define addresses fact"
set_fact:
address: "{{ groups['all'] | map('extract', hostvars) | list | json_query('[*].network.address') }}"
Это намного короче, может быть, проще.
Я хотел бы спросить, могу ли я использовать map
и extract
и «список диктов», прежде чем сгладить список, чтобы «отфильтровать» все элементы, где network.address не определен, и использовать json_query вместе с некоторой строковой операцией для добавления '-псевдоним'. Есть ли аналогичный простой способ заменить первый скрипт?
В чистом виде JMESPath, учитывая JSON
[
{
"infrastructure": {"name": "x..."},
"network": {"address": "1..."}
},
{
"infrastructure": {"name": "y..."}
},
{
"infrastructure": {"name": "z..."},
"network": {"address": "2..."}
},
{
"infrastructure": {"name": "a..."},
"network": {}
}
]
Вы можете извлечь infrastructure.name
, объединенный с -alias
, с установленным network.address
следующим образом:
[?network.address].join('-', [infrastructure.name, 'alias'])
Это даст:
[
"x...-alias",
"z...-alias"
]
Функция join в первую очередь предназначена для склеивания элементов массива, но ее также можно использовать для конкатенации строк.
И так для playbook, демонстрирующего это:
- hosts: all
gather_facts: no
tasks:
- debug:
msg: >-
{{
servers | to_json | from_json |
json_query(
'[?network.address].join(`-`, [infrastructure.name, `alias`])'
)
}}
vars:
servers:
- infrastructure:
name: x...
network:
address: 1...
- infrastructure:
name: y...
- infrastructure:
name: z...
network:
address: 2...
- infrastructure:
name: a...
network:
Note that the odd construct | from_json | to_json
is explained in this other answer
Это дает:
PLAY [all] ********************************************************************************************************
TASK [debug] ******************************************************************************************************
ok: [localhost] => {
"msg": [
"x...-alias",
"z...-alias"
]
}
PLAY RECAP ********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Ах, да, действительно, это проблема с данными YAML / Ansible, если у вас есть это как факт из предыдущей задачи / хоста, то вам должно быть хорошо без этого.
В моем случае «серверный» dict является частью hostvars, и, похоже, мне не нужно фильтровать
| to_json | from_json
, но он работает, как и ожидалось.