Использование Ansible set_fact для создания словаря из результатов регистра
В Ansible я использовал register
сохранить результаты задачи в переменной people
, Опуская материал, который мне не нужен, он имеет такую структуру:
{
"results": [
{
"item": {
"name": "Bob"
},
"stdout": "male"
},
{
"item": {
"name": "Thelma"
},
"stdout": "female"
}
]
}
Я хотел бы использовать последующий set_fact
Задача создать новую переменную со словарем:
{
"Bob": "male",
"Thelma": "female"
}
Я думаю, что это возможно, но я иду кругами, но пока безуспешно.
2 ответа
Я думаю, что я добрался там в конце.
Задача такова:
- name: Populate genders
set_fact:
genders: "{{ genders|default({}) | combine( {item.item.name: item.stdout} ) }}"
with_items: "{{ people.results }}"
Это проходит через каждый из диктов (item
) в people.results
массив, каждый раз создавая новый dict как {Bob: "male"}
, а также combine()
Это новый диктат в genders
массив, который заканчивается как:
{
"Bob": "male",
"Thelma": "female"
}
Предполагается, что ключи (name
в этом случае) будет уникальным.
Затем я понял, что на самом деле мне нужен список словарей, так как, кажется, гораздо проще использовать with_items
:
- name: Populate genders
set_fact:
genders: "{{ genders|default([]) + [ {'name': item.item.name, 'gender': item.stdout} ] }}"
with_items: "{{ people.results }}"
Это продолжает объединять существующий список со списком, содержащим один диктант. Мы заканчиваем с genders
массив, как это:
[
{'name': 'Bob', 'gender': 'male'},
{'name': 'Thelma', 'gender': 'female'}
]
Спасибо, Фил, за ваше решение; на случай, если кто-нибудь попадет в ту же ситуацию, что и я, вот (более сложный) вариант:
---
# this is just to avoid a call to |default on each iteration
- set_fact:
postconf_d: {}
- name: 'get postfix default configuration'
command: 'postconf -d'
register: command
# the answer of the command give a list of lines such as:
# "key = value" or "key =" when the value is null
- name: 'set postfix default configuration as fact'
set_fact:
postconf_d: >
{{
postconf_d |
combine(
dict([ item.partition('=')[::2]|map('trim') ])
)
with_items: command.stdout_lines
Это даст следующий вывод (зачеркнут для примера):
"postconf_d": {
"alias_database": "hash:/etc/aliases",
"alias_maps": "hash:/etc/aliases, nis:mail.aliases",
"allow_min_user": "no",
"allow_percent_hack": "yes"
}
Если пойти еще дальше, проанализируйте списки в 'value':
- name: 'set postfix default configuration as fact'
set_fact:
postconf_d: >-
{% set key, val = item.partition('=')[::2]|map('trim') -%}
{% if ',' in val -%}
{% set val = val.split(',')|map('trim')|list -%}
{% endif -%}
{{ postfix_default_main_cf | combine({key: val}) }}
with_items: command.stdout_lines
...
"postconf_d": {
"alias_database": "hash:/etc/aliases",
"alias_maps": [
"hash:/etc/aliases",
"nis:mail.aliases"
],
"allow_min_user": "no",
"allow_percent_hack": "yes"
}
Несколько вещей, на которые стоит обратить внимание:
в этом случае нужно все "обрезать" (используя
>-
в ЯМЛ и-%}
в Jinja), иначе вы получите ошибку вроде:FAILED! => {"failed": true, "msg": "|combine expects dictionaries, got u\" {u'...
очевидно,
{% if ..
далеко от пуленепробиваемогов случае с постфиксом,
val.split(',')|map('trim')|list
можно было бы упростить доval.split(', ')
, но я хотел бы отметить тот факт, что вам нужно будет|list
в противном случае вы получите ошибку вроде:"|combine expects dictionaries, got u\"{u'...': <generator object do_map at ...
Надеюсь, это поможет.