Utiliser un ensemble de faits possible pour créer un dictionnaire à partir des résultats de registre
Dans Ansible j'ai utilisé register
pour enregistrer les résultats d'une tâche dans la variable people
. Omettant les choses dont je n'ai pas besoin, il a cette structure:
{
"results": [
{
"item": {
"name": "Bob"
},
"stdout": "male"
},
{
"item": {
"name": "Thelma"
},
"stdout": "female"
}
]
}
j'aimerais utiliser un subséquente set_fact
tâche pour générer une nouvelle variable avec un dictionnaire comme ceci:
{
"Bob": "male",
"Thelma": "female"
}
je suppose que c'est peut-être possible, mais je tourne en rond sans succès jusqu'à présent.
2 réponses
je crois que j'y suis arrivé à la fin.
La tâche est comme ceci:
- name: Populate genders
set_fact:
genders: "{{ genders|default({}) | combine( {item.item.name: item.stdout} ) }}"
with_items: "{{ people.results }}"
Il parcourt chacun des dicts (item
)people.results
array, chaque fois la création d'un nouveau dict comme {Bob: "male"}
et combine()
s que de nouvelles dict dans le genders
array, qui se termine comme:
{
"Bob": "male",
"Thelma": "female"
}
Il assume les touches (name
dans ce cas) sera unique.
j'ai alors réalisé que je voulais une liste de dictionnaires, comme il semble beaucoup plus facile à Boucler en utilisant with_items
:
- name: Populate genders
set_fact:
genders: "{{ genders|default([]) + [ {'name': item.item.name, 'gender': item.stdout} ] }}"
with_items: "{{ people.results }}"
cela permet de combiner la liste existante avec une liste contenant un seul dict. Nous nous retrouvons avec un genders
array comme ceci:
[
{'name': 'Bob', 'gender': 'male'},
{'name': 'Thelma', 'gender': 'female'}
]
Merci Phil pour votre solution; au cas où quelqu'un se retrouve dans la même situation que moi, voici une variante (plus complexe):
---
# 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
ceci donnera la sortie suivante (stripped pour l'exemple):
"postconf_d": {
"alias_database": "hash:/etc/aliases",
"alias_maps": "hash:/etc/aliases, nis:mail.aliases",
"allow_min_user": "no",
"allow_percent_hack": "yes"
}
aller encore plus loin, parcourir les listes dans la 'valeur':
- 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"
}
A peu de choses à noter:
dans ce cas, il est nécessaire de "couper" tout (en utilisant le
>-
YAML et-%}
Jinja), sinon vous obtiendrez une erreur comme:FAILED! => {"failed": true, "msg": "|combine expects dictionaries, got u\" {u'...
évidemment, le
{% if ..
est loin d'être à l'épreuve des ballesdans le cas de postfix,
val.split(',')|map('trim')|list
aurait pu être simplifié àval.split(', ')
, mais je voulais souligner le fait que vous aurez besoin d'|list
sinon vous obtiendrez une erreur comme:"|combine expects dictionaries, got u\"{u'...': <generator object do_map at ...
j'Espère que cela peut aider.