Dans Ansible, j'ai utilisé register
pour enregistrer les résultats d'une tâche dans la variable people
. En 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 une tâche set_fact
ultérieure pour générer une nouvelle variable avec un dictionnaire comme celui-ci:
{
"Bob": "male",
"Thelma": "female"
}
Je suppose que cela pourrait être possible, mais je tourne en rond sans succès jusqu'ici.
Je pense que je suis arrivé à la fin.
La tâche est la suivante:
- name: Populate genders
set_fact:
genders: "{{ genders|default({}) | combine( {item.item.name: item.stdout} ) }}"
with_items: "{{ people.results }}"
Il parcourt chacun des dict (item
) du tableau people.results
, créant à chaque fois un nouveau dict, comme {Bob: "male"}
, et combine()
s ce nouveau dict dans le genders
tableau, qui se termine comme:
{
"Bob": "male",
"Thelma": "female"
}
Il suppose que les clés (la name
dans ce cas) seront uniques.
J'ai alors réalisé que je voulais en fait une liste de dictionnaires, car il me semble beaucoup plus facile de parcourir en utilisant with_items
:
- name: Populate genders
set_fact:
genders: "{{ genders|default([]) + [ {'name': item.item.name, 'gender': item.stdout} ] }}"
with_items: "{{ people.results }}"
Ceci continue à combiner la liste existante avec une liste contenant un seul dict. Nous nous retrouvons avec un tableau genders
comme ceci:
[
{'name': 'Bob', 'gender': 'male'},
{'name': 'Thelma', 'gender': 'female'}
]
Merci Phil pour votre solution. au cas où quelqu'un se retrouverait 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
Cela donnera le résultat suivant (dépouillé 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"
}
Pour aller encore plus loin, analysez 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"
}
Quelques points à noter:
dans ce cas, il faut tout "rogner" (en utilisant >-
dans YAML et -%}
dans Jinja ), sinon vous aurez obtenir une erreur comme:
FAILED! => {"failed": true, "msg": "|combine expects dictionaries, got u\" {u'...
évidemment le {% if ..
est loin d'être à l'épreuve des balles
dans le cas suivant, val.split(',')|map('trim')|list
aurait pu être simplifié à val.split(', ')
, mais je voulais souligner le fait que vous aurez besoin de |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.