47 votes

Ajouter à des listes ou ajouter des clés à des dictionnaires dans Ansible

(En lien avec Callbacks ou crochets, et séries réutilisables de tâches, dans les rôles Ansible):

Y a-t-il un moyen meilleur pour ajouter à une liste ou ajouter une clé à un dictionnaire dans Ansible que d'utiliser une expression de modèle jinja2?

Je sais que vous pouvez faire quelque chose comme:

- name: c'est une astuce
  shell: echo "{% originalvar.append('x') %}La nouvelle valeur de originalvar est {{originalvar}}"

mais n'y a-t-il vraiment pas une sorte de tâche ou aide pour le faire?

Cela semble fragile, semble être non documenté, et repose sur de nombreuses hypothèses sur le fonctionnement des variables dans Ansible.

Mon cas d'utilisation est plusieurs rôles (extensions de serveur de base de données) qui doivent chacun fournir une certaine configuration à un rôle de base (le serveur de base de données). Ce n'est pas aussi simple que d'ajouter une ligne au fichier de configuration du serveur de base de données; chaque changement s'applique à la même ligne, par exemple, les extensions bdr et pg_stat_statements doivent toutes deux apparaître sur une ligne cible:

shared_preload_libaries = 'bdr, pg_stat_statements'

Est-ce la méthode Ansible pour le faire de simplement traiter le fichier de configuration plusieurs fois (une fois par extension) avec une expression régulière qui extrait la valeur actuelle, l'analyse, puis la réécrit? Si oui, comment le rendre idempotent sur plusieurs exécutions?

Que se passe-t-il si la config est plus difficile à analyser et ce n'est pas aussi simple que d'ajouter une autre valeur séparée par des virgules? Pensez aux fichiers de configuration XML.

50voto

Max Kovgan Points 571

Depuis Ansible v2.x vous pouvez faire ceci:

# cas d'utilisation I: ajout à une variable LIST:

      - name: mon ajouteur
        set_fact:
          my_list_var: '{{my_list_myvar + new_items_list}}'

# cas d'utilisation II: ajout à une variable LIST un par un:

      - name: mon ajouteur
        set_fact:
          my_list_var: '{{my_list_var + [item]}}'
        with_items: '{{my_new_items|list}}'

# cas d'utilisation III: ajout de plusieurs clés à une variable DICT en "batch":

      - name: mon ajouteur
        set_fact:
          my_dict_var: '{{my_dict_var|combine(my_new_keys_in_a_dict)}}'

# cas d'utilisation IV: ajout de clés à une variable DICT un par un à partir de tuples
      - name: configurer une liste de tuples (pour 2.4.x et plus
        set_fact:
          lot: >
            [('key1', 'value1',), ('key2', 'value2',), ..., ('keyN', 'valueN',)],
      - name: mon ajouteur
        set_fact:
          my_dict_var: '{{my_dict_var|combine({item[0]: item[1]})}}'
        with_items: '{{lot}}'
# cas d'utilisation V: ajout de clés à une variable DICT un par un à partir d'une liste de dicts (merci à @ssc)

  - name: ajouter de nouvelles paires clé / valeur au dict
    set_fact:
      my_dict_var: "{{ my_dict_var | combine({item.key: item.value}) }}"
    with_items:
    - { key: 'key01', value: 'value 01' }
    - { key: 'key02', value: 'value 03' }
    - { key: 'key03', value: 'value 04' }

tous les cas ci-dessus sont documentés dans: http://docs.ansible.com/ansible/playbooks_filters.html

19voto

GnP Points 945

Vous pouvez fusionner deux listes dans une variable avec +. Par exemple, si vous avez un fichier group_vars avec ce contenu :

---
# group_vars/all
pgsql_extensions:
  - ext1
  - ext2
  - ext3

Et qu'il est utilisé dans un template pgsql.conf.j2 comme suit :

# {{ ansible_managed }}
pgsql_extensions={% for item in pgsql_extensions %}{{ item }}, {% endfor %}

Vous pouvez ensuite ajouter des extensions aux serveurs de base de données de test de cette manière :

---
# group_vars/testing_db
append_exts:
  - ext4
  - ext5
pgsql_extensions: "{{ pgsql_extensions + append_exts }}"

Lorsque le rôle est exécuté sur l'un des serveurs de test, les extensions additionnelles seront ajoutées.

Je ne suis pas sûr que cela fonctionne également pour les dictionnaires, et faites également attention aux espaces et au fait de laisser une virgule pendante à la fin de la ligne.

3voto

Arthur Tsang Points 31

Vous devez diviser la boucle en 2

\--- 
- hosts: localhost
  tasks: 
    - include\_vars: stacks
    - set\_facts: roles={{stacks.Roles | split(' ')}}
    - include: addhost.yml
      with\_items: "{{roles}}"

et addhost.yml

\- set\_facts: groupname={{item}}
- set\_facts: ips={{stacks\[item\]|split(' ')}}
- local\_action: add\_host hostname={{item}} groupname={{groupname}}
  with\_items: {{ips}}

3voto

CJ Dennis Points 199

Presque toutes les réponses ici nécessitent des changements dans les tâches, mais j'avais besoin de fusionner dynamiquement des dictionnaires dans la définition des variables, pas pendant l'exécution.

Par exemple, je veux définir certaines variables partagées dans les all group_vars et ensuite je veux les étendre dans un autre group ou host_vars. Très utile quand on travaille pour des rôles.

Si vous essayez d'utiliser les filtres combine ou union en écrasant la variable d'origine dans les fichiers de variables, vous vous retrouverez dans une boucle infinie lors du templating, donc j'ai créé cette solution de contournement (ce n'est pas une solution).

Vous pouvez définir plusieurs variables en fonction d'un motif de nom et ensuite les charger automatiquement dans le rôle.

group_vars/all.yml

dictionnaire_de_bla:
  - name: blabla
    valeur1 : blabla
    valeur2 : blabla

group_vars/group1.yml

dictionnaire_de_bla_groupe1:
  - name: blabla2
    valeur1 : blabla2
    valeur2 : blabla2

Extrait de code du rôle

tâches:
  - name: Exécuter pour toutes les variations dictionary_of_bla.*
    include_tasks: do_some_stuff.yml
    with_items: "{{ lookup('varnames','dictionary_of_bla.*').split(',') }}"
    loop_control:
      loop_var: _dictionary_of_bla

do_some_stuff.yml

- name: faire de la magie
  magic:
    trick_name: item.name
    trick_value1: item.value1
    trick_value2: item.value2
  with_items: "{{ vars[_dictionary_of_bla] }}"

C'est juste un extrait, mais vous devriez comprendre comment cela fonctionne. note: lookup('varnames','') est disponible depuis ansible 2.8

Je suppose qu'il serait également possible de fusionner toutes les variables dictionnaire_de_bla.* en un seul dictionnaire pendant l'exécution en utilisant le même lookup.

L'avantage de cette approche est que vous n'avez pas besoin de définir des listes exactes de noms de variables, mais seulement le motif et l'utilisateur peut le définir dynamiquement.

2voto

nuts Points 285

Pas sûr quand ils ont ajouté ceci, mais au moins pour les dictionnaires/hachages (pas les listes/tableaux), vous pouvez définir la variable hash_behaviour, comme ceci : hash_behaviour = merge dans votre ansible.cfg.

Il m'a fallu pas mal d'heures pour tomber accidentellement sur ce paramètre :S

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X