[Petite histoire] Deux façons de fusionner correctement les variables Ansible et dict imbriquées

Comment fusionner des variables avec Ansible?

Ansible utilise Jinja2 pour le filtre des variables.

Souhaitez-vous parfois fusionner en profondeur des variables, y compris des dictionnaires? Aujourd'hui, je vais vous présenter deux façons de réaliser une fusion profonde de variables avec Ansible. Ce qui est différent, c'est que la méthode est différente pour Ansible 2.0 et supérieur et inférieur.

Pour la version Ansible "> = 2.0"

Un nouveau ** filtre de combinaison ** a été implémenté depuis Ansible 2.0. http://docs.ansible.com/ansible/playbooks_filters.html#combining-hashes-dictionaries

test.yml


- hosts: localhost
  gather_facts: no
  vars:
    dict:
      foo:
        bar: 1
    dict2:
      foo:
        baz: 2
      qux: 2
    # combining hashes/dictionaries (new in version 2.0)
    dict_combine: "{{ dict | combine(dict2, recursive=True) }}"
  tasks:
    - debug:
        var: dict_combine

Si vous faites ce qui précède, vous obtiendrez les résultats suivants.

$ ansible-playbook -i localhost, test.yml

PLAY [localhost] ***************************************************************

TASK [debug] *******************************************************************
ok: [localhost] => {
    "dict_combine": {
        "foo": {
            "bar": 1,
            "baz": 2
        },
        "qux": 2
    }
}

C'est très pratique. Le contenu de dict-> foo est correctement fusionné. À propos, omettre «récursif» n'est qu'une fusion de mise à jour.

recursive=false


$ ansible-playbook -i localhost, test.yml

PLAY [localhost] ***************************************************************

TASK [debug] *******************************************************************
ok: [localhost] => {
    "dict_combine": {
        "foo": {
            "baz": 2
        },
        "qux": 2
    }
}

À propos, la fonction équivalente à combiner qui omet récursive pourrait être réalisée comme suit en utilisant update () même dans la version antérieure à Ansible 2.0.

test.yml


- hosts: localhost
  gather_facts: no
  vars:
    dict:
      foo:
        bar: 1
    dict2:
      foo:
        baz: 2
      qux: 2
    # **[Note]**
    # jinja2 'do' tag need expression-statement extension
    # please set below to [default] section in ansible.cfg
    # jinja2_extensions=jinja2.ext.do
    dict_update: |
      {% do dict.update(dict2) %}
      {{ dict }}
  tasks:
    - debug:
        var: dict_update

Pour la version Ansible "<2.0"

Si vous devez utiliser Ansible 1.X pour diverses raisons, c'est un peu gênant car vous ne pouvez pas utiliser combiner. Bien sûr, vous pouvez importer la partie source de la moissonneuse-batteuse implémentée en 2.0, mais comme c'est un gros problème, j'aimerais créer mon propre plugin Filter. http://docs.ansible.com/ansible/developing_plugins.html#filter-plugins

Pour utiliser le plugin, mettez le chemin (filter_plugins =) dans ansible.cfg ou placez le fichier personnalisé directement dans le répertoire plugin d'ansible lui-même et il sera lu lors de l'exécution de ansible.

/path-to/ansible/filter_plugins/dict_merge.py


from copy import deepcopy


def dict_merge(a, b):
    if not isinstance(b, dict):
        return b
    result = deepcopy(a)
    for k, v in b.iteritems():
        if k in result and isinstance(result[k], dict):
                result[k] = dict_merge(result[k], v)
        else:
            result[k] = deepcopy(v)
    return result


class FilterModule(object):
    def filters(self):
        return {'dict_merge': dict_merge}

Ceci termine la création du plug-in. C'est plus facile que vous ne le pensez. Testons-le.

test.yml


- hosts: localhost
  gather_facts: no
  vars:
    dict:
      foo:
        bar: 1
    dict2:
      foo:
        baz: 2
      qux: 2
    # custom filter plugin
    dict_merged: "{{ dict | dict_merge(dict2) }}"
  tasks:
    - debug:
        var: dict_merged

Le résultat de l'exécution est le suivant.

$ ansible-playbook -i localhost, test.yml

PLAY [localhost] ***************************************************************

TASK [debug] *******************************************************************
ok: [localhost] => {
    "dict_merged": {
        "foo": {
            "bar": 1,
            "baz": 2
        },
        "qux": 2
    }
}

J'ai pu obtenir le même résultat que combiner. Bien sûr, il fusionne également des dicts plus profonds.

  1. Si vous souhaitez utiliser la fusion profonde rapidement dans le système X, il existe un tel moyen, alors pourquoi ne pas l'envisager?

Autre

En plus du plug-in Filter, Ansible propose plusieurs plug-ins que vous pouvez créer vous-même. http://docs.ansible.com/ansible/developing_plugins.html

Les plug-ins de recherche et de rappel sont relativement informatifs et contiennent des informations sur les personnes qui font diverses choses.

Let's enjoy your Ansible life!

Recommended Posts

[Petite histoire] Deux façons de fusionner correctement les variables Ansible et dict imbriquées
Deux façons de rendre Ansible portable