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

34

(Liés à des rappels ou des points d' ancrage, et à une série de tâches réutilisables, dans des rôles Ansible ):

Y at-il un meilleur moyen d’ajouter à une liste ou d’ajouter une clé à un dictionnaire dans Ansible que (ab) en utilisant une expression de modèle jina2?

Je sais que tu peux faire quelque chose comme:

- name: this is a hack
  shell: echo "{% originalvar.append('x') %}New value of originalvar is {{originalvar}}"

mais n'y a-t-il vraiment aucune sorte de méta-tâche ou d'aide pour le faire?

Il se sent fragile, semble ne pas avoir de papiers d'identité et repose sur de nombreuses hypothèses sur le fonctionnement des variables dans Ansible.

Mon cas d'utilisation est constitué de plusieurs rôles (extensions de serveur de base de données) qui doivent chacun fournir une 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 bdret pg_stat_statementsdoit apparaître sur une ligne cible:

shared_preload_libaries = 'bdr, pg_stat_statements'

Pour ce faire, Ansible peut-il simplement traiter le fichier de configuration plusieurs fois (une fois par extension) avec une expression rationnelle qui extrait la valeur actuelle, la analyse, puis la réécrit? Si oui, comment rend-on cet idempotent après plusieurs exécutions?

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

Craig Ringer
la source
Tu sais quoi? J'aime la coupe de votre foc à effets secondaires ☺
DomQ

Réponses:

13

Vous pouvez fusionner deux listes dans une variable avec +. Disons que vous avez un group_varsfichier avec ce contenu:

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

Et il est utilisé dans un modèle pgsql.conf.j2comme:

# {{ 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 la manière suivante:

---
# 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 supplémentaires sont ajoutées.

Je ne suis pas sûr que cela fonctionne également pour les dictionnaires, et que vous fassiez également attention aux espaces et en laissant une virgule au bout de la ligne.

GnP
la source
Vous pouvez le faire, mais vous devez tout faire group_vars, les rôles ne peuvent pas prendre en charge les détails de la configuration des extensions elles-mêmes. Il s'agit d'ajouter des variables à partir de rôles que je recherche particulièrement, afin qu'un rôle puisse être ajouté à un var exposé par un autre rôle.
Craig Ringer le
Votre rôle de base connaît-il chaque rôle d'extension? J'ai eu un cas similaire où j'ai pu laisser la concaténation jusqu'à une with_itemsphrase.
GnP
non, et c'est vraiment le problème. Dans un déploiement, le rôle de base pourrait être al
Craig Ringer le
4
Il semble que si vous essayez de concaténer deux listes, le modèle est considéré comme un modèle infiniment récursif, car le côté gauche se trouve également à droite. Est-ce que je comprends mal comment l'utiliser?
Ibrahim
2
@spectras Au moins à partir de la version 2.7, cela ne fonctionne PAS. Comme Ibrahim l'a suggéré, cela provoque une erreur: "boucle récursive détectée dans la chaîne de modèle".
rluba
35

Depuis Ansible v2.x, vous pouvez faire ceci:

# use case I: appending to LIST variable:

      - name: my appender
        set_fact:
          my_list_var: '{{my_list_myvar + new_items_list}}'

# use case II: appending to LIST variable one by one:

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

# use case III: appending more keys DICT variable in a "batch":

      - name: my appender
        set_fact:
          my_dict_var: '{{my_dict_var|combine(my_new_keys_in_a_dict)}}'

# use case IV: appending keys DICT variable one by one from tuples
      - name: setup list of tuples (for 2.4.x and up
        set_fact:
          lot: >
            [('key1', 'value1',), ('key2', 'value2',), ..., ('keyN', 'valueN',)],
      - name: my appender
        set_fact:
          my_dict_var: '{{my_dict_var|combine({item[0]: item[1]})}}'
        with_items: '{{lot}}'
# use case V: appending keys DICT variable one by one from list of dicts (thanks to @ssc)

  - name: add new key / value pairs to 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' }

tout ce qui précède est documenté dans: http://docs.ansible.com/ansible/playbooks_filters.html

Max Kovgan
la source
1
cas d'utilisation IV juste ajouteu'(': u\"'\"}"
ssc
1
merci, @ssc. J'ai remarqué que ça ne marche pas avec ansible 2.4.x(FIXED)
Max Kovgan le
selon USECASE # 4, j'ai ajouté la valeur par défaut pour gérer l' erreur non définie dans mon scénario: set_fact: my_dict_var: '{{my_dict_var|default({})|combine({item[0]: item[1]})}}'. L'erreur non définie survient lorsqu'un filtrage est utilisé ou qu'aucun résultat n'est enregistré.
SK Venkat
Monsieur SK Venkat, l'exemple de code présenté ici ne montre que des choses très spécifiques (ajout d'éléments de dictionnaire à partir de tuples). Si vous devez faire autre chose, ce code n'est pas votre copier-coller.
Max Kovgan le
3

vous devez diviser la boucle en 2

--- 
- hôtes: localhost
  les tâches: 
    - include_vars: piles
    - set_facts: roles = {{stacks.Roles | Divisé(' ')}}
    - inclure: addhost.yml
      with_items: "{{rôles}}"

et addhost.yml

- set_facts: groupname = {{item}}
- set_facts: ips = {{stacks [item] | split ('')}}
- local_action: add_host hostname = {{item}} groupname = {{groupname}}
  with_items: {{ips}}
Arthur Tsang
la source
1

Vous ne savez pas quand ils ont ajouté cela, mais au moins pour les dictionnaires / hachages (PAS les listes / tableaux), vous pouvez définir la variable hash_behaviour , comme ceci: hash_behaviour = mergedans votre ansible.cfg.

Il m'a fallu plusieurs heures pour tomber par hasard sur ce réglage: S

des noisettes
la source
c’est très pratique, mais méfiez-vous de l’activer sur un code existant. peut casser des œufs.
Max Kovgan
0

Presque toutes les réponses ici nécessitent des modifications dans les tâches, mais je devais fusionner de manière dynamique les dictionnaires dans la définition de vars, pas pendant l'exécution.

Par exemple, je veux définir des vars partagés dans all group_varspuis je veux les étendre dans un autre groupou host_vars. Très utile lorsque vous travaillez pour des rôles.

Si vous essayez d'utiliser le combineou les unionfiltres écrasant la variable d'origine dans les fichiers var, vous vous retrouverez avec une boucle infinie lors de la création de modèles. J'ai donc créé cette solution de contournement (ce n'est pas une solution).

Vous pouvez définir plusieurs variables en fonction d'un modèle de nom, puis les charger automatiquement dans le rôle.

group_vars/all.yml

dictionary_of_bla:
  - name: blabla
    value1 : blabla
    value2 : blabla

group_vars/group1.yml

dictionary_of_bla_group1:
  - name: blabla2
    value1 : blabla2
    value2 : blabla2

extrait de code de rôle

tasks:
  - name: Run for all dictionary_of_bla.* variations
    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: do magic
  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 avoir une idée de la façon dont cela fonctionne. remarque: la recherche ('varnames', '') est disponible depuis ansible 2.8

Je suppose qu'il serait également possible de fusionner toutes les variables dictionary_of_bla.*dans un dictionnaire lors de l'exécution à l'aide de la même recherche.

L'avantage de cette approche est qu'il n'est pas nécessaire de définir des listes exactes de noms de variables, mais seuls le modèle et l'utilisateur peuvent le définir de manière dynamique.

VeselaHouba
la source
-4

Ansibleest un système d’automatisation et, en ce qui concerne la gestion des fichiers de configuration, il n’est pas très différent de apt. La raison pour laquelle de plus en plus de logiciels offrent la possibilité de lire des extraits de configuration à partir d'un conf.drépertoire est de permettre à ces systèmes d'automatisation d'avoir différents packages / rôles pour ajouter une configuration au logiciel. Je crois que ce n’est pas la philosophie de Ansiblefaire ce que vous avez en tête, mais plutôt d’utiliser le conf.dtruc. Si le logiciel en cours de configuration n'offre pas cette fonctionnalité, vous risquez d'avoir des problèmes.

Puisque vous parlez de fichiers de configuration XML, j'en profite pour me plaindre. Il existe une raison pour la tradition Unix d’utiliser des fichiers de configuration en texte brut. Les fichiers de configuration binaires ne se prêtent pas bien à l'automatisation du système. Par conséquent, tout type de format binaire vous causera des problèmes et vous obligera probablement à créer un programme pour gérer la configuration. (Si quelqu'un pense que XML est un format de texte brut, il devrait aller faire examiner son cerveau.)

Maintenant, sur votre PostgreSQLproblème spécifique . PostgreSQLsupporte le conf.dtruc. Tout d'abord, je voudrais vérifier si shared_preload_librariespeut être spécifié plusieurs fois. Je n'ai trouvé aucune indication dans la documentation, mais je voudrais quand même l'essayer. S'il ne peut pas être spécifié plusieurs fois, j'expliquerai mon problème aux PostgreSQLgars au cas où ils auraient des idées; c'est un PostgreSQLproblème et non un Ansibleproblème. S'il n'y avait pas de solution et que je ne pouvais vraiment pas fusionner les différents rôles, je mettrais en place un système pour compiler la configuration sur l'hôte géré. Dans ce cas, je serais probablement créer un script /usr/local/sbin/update_postgresql_configqui compilerait /etc/postgresql/postgresql.conf.jinjadans /etc/postgresql/9.x/main/postgresql.conf. Le script lirait les bibliothèques de préchargement partagées à partir de /etc/postgresql/shared_preload_libraries.txt, une bibliothèque par ligne, et les fournirait à jinja.

Il n’est pas rare que les systèmes d’automatisation fassent cela. Un exemple est le exim4paquet Debian .

Antonis Christofides
la source
PostgreSQL ™ supporte un conf.dmécanisme d'inclusion et utilise heureusement les fichiers en texte clair. Cependant, il existe certaines options de configuration où plusieurs extensions peuvent avoir des opinions à ce sujet - par exemple, "augmenter max_wal_senders de 10 par rapport à ce qu'il était auparavant".
Craig Ringer le
4
Vous semblez dire que l'application doit être modifiée pour contourner les limites du système de gestion de la configuration, ou que je devrais renoncer à avoir des rôles réutilisables.
Craig Ringer