2016-02-24 10 views
29

In Ansible ho utilizzato register per salvare i risultati di un'attività nella variabile people. Tralasciando la roba non ho bisogno, si ha questa struttura:Utilizzo di Ansize set_fact per creare un dizionario dai risultati del registro

{ 
    "results": [ 
     { 
      "item": { 
       "name": "Bob" 
      }, 
      "stdout": "male" 
     }, 
     { 
      "item": { 
       "name": "Thelma" 
      }, 
      "stdout": "female" 
     } 
    ] 
} 

mi piacerebbe utilizzare un successivo compito set_fact per generare una nuova variabile con un dizionario come questo:

{ 
    "Bob": "male", 
    "Thelma": "female" 
} 

I Immagino che questo potrebbe essere possibile, ma sto andando in tondo senza fortuna fino ad ora.

+0

Si noti che, poiché ansible v2.2, with_items richiede il jinja2 wrapping esplicito. Quindi il primo esempio sarebbe:

 - name: Populate genders set_fact: genders: "{{ genders|default({}) | combine({item.item.name: item.stdout}) }}" with_items: "{{ people.results }}" 
sarraz

risposta

51

Penso di esserci arrivato alla fine.

Il compito è così:

- name: Populate genders 
    set_fact: 
    genders: "{{ genders|default({}) | combine({item.item.name: item.stdout}) }}" 
    with_items: "{{ people.results }}" 

Esso scorre ciascuna delle dicts (item) nella matrice people.results, ogni volta creando un nuovo dict come {Bob: "male"} e combine() s quel nuovo dict nel genders matrice, che finisce come:

{ 
    "Bob": "male", 
    "Thelma": "female" 
} 

assume i tasti (il name in questo caso) saranno unici.


Poi ho capito che in realtà volevo una lista di dizionari, come sembra molto più facile per eseguire il ciclo utilizzando with_items:

- name: Populate genders 
    set_fact: 
    genders: "{{ genders|default([]) + [ {'name': item.item.name, 'gender': item.stdout} ] }}" 
    with_items: "{{ people.results }}" 

Ciò mantiene combinando l'elenco esistente con un elenco contenente un singolo dict.Finiamo con una gamma genders come questo:

[ 
    {'name': 'Bob', 'gender': 'male'}, 
    {'name': 'Thelma', 'gender': 'female'} 
] 
+5

È possibile evitare di dichiarare un dizionario vuoto se si utilizza il filtro _default_. 'sessi:" {{genders | default ({}) | combine ({item.item.name: item.stdout})}} ' – smicyk

+2

Grazie @smicyk - Ho tardivamente aggiunto il tuo suggerimento pratico negli esempi –

+1

Secondo esempio dovrebbe essere: genders: '" {{genders | default ([]) + [{'name': item.item.name, 'gender': item.stdout}]}} "', con una matrice predefinita invece di una oggetto predefinito –

8

Grazie Phil per la soluzione; nel caso in cui qualcuno ottiene sempre nella stessa situazione come me, ecco una (più complessa) Variante:

--- 
# 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 

Questo darà il seguente output (spogliata per l'esempio):

"postconf_d": { 
    "alias_database": "hash:/etc/aliases", 
    "alias_maps": "hash:/etc/aliases, nis:mail.aliases", 
    "allow_min_user": "no", 
    "allow_percent_hack": "yes" 
} 

Andando ancora inoltre, analizzare le liste del 'valore':

- 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" 
} 

alcune cose da notare:

  • in questo caso è necessaria per "trim" tutto (utilizzando il >- in YAML e -%} in Jinja), altrimenti si ottiene un errore del tipo:

    FAILED! => {"failed": true, "msg": "|combine expects dictionaries, got u\" {u'... 
    
  • ovviamente il {% if .. è lontano

  • nel caso postfix, val.split(',')|map('trim')|list potrebbe essere stato semplificato in val.split(', '), ma volevo sottolineare il fatto è necessario |list altrimenti si ottiene un errore del tipo:

    "|combine expects dictionaries, got u\"{u'...': <generator object do_map at ... 
    

Spero che questo può aiutare.

+0

Molto serbatoi per la nota '-%}' Ho litigato con questo per settimane – ceving

+1

Questo è un grande Suggerimento: solo una nota, con Ansible 2.4.3 almeno ogni riga tranne l'ultima ha bisogno di '{% - ... -%}' con il trattino su entrambe le parentesi aperta e chiusa, altrimenti il ​​dict verrà interpretato come una stringa – Michael

Problemi correlati