web-dev-qa-db-fra.com

Utiliser Ansible set_fact pour créer un dictionnaire à partir des résultats du registre

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.

55
Phil Gyford

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'}
]
97
Phil Gyford

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.

11
bufh