Aller au contenu

Ecrire des plugins de filtres Ansible

logo ansible

Parfois, manipuler des données directement dans les playbooks Ansible peut être fastidieux. C’est là que les filtres Ansible interviennent.

Les plugins de filtres vont vous permettre de gérer vos données directement avec du code Python. Une fois que vous saurez comment le faire, vous ne pourrez plus vous en passer.

Passons au code pour transformer une string SnakeCaze à une CamelCaze

Nous voulons transformer une string formatée en snake_case à une autre en CamelCase. Écrivons une simple boucle qui va itérer sur chaque élément d’un split avec le caractère ’_’, les passer en majuscule et concaténer le tout.

def snake_to_camel(text):
words = text.split('_')
return words[0].lower() + ''.join(word.title() for word in words[1:])

Écriture du plugin de filtre Ansible

Il suffit d’ajouter à la fin de votre programme python la définition de la classe FilterModule.

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

Ensuite enregistrer vos programmes dans le répertoire filter_plugins. Si vous voulez utiliser un autre répertoire, il faudra l’indiquer dans votre fichier ansible.cfg

[default]
filter_plugins = /path/to/filter/plugins

Utilisation de votre filtre dans un playbook

Rien de plus simple. Écrivez ceci dans un fichier que vous nommerez test.yml

---
- hosts: localhost
gather_facts: false
vars:
text1: "une_variable"
text2: "une_autre_variable"
tasks:
- name: Snake To camel
ansible.builtin.debug:
msg: "{{ item | snaketocamel }}"
with_items:
- "{{text1}}"
- "{{text2}}"

Vous pouvez le lancer avec la commande :

Terminal window
ansible-playbook -i localhost -c local test.yml

Ce qui vous donnera :

ok: [localhost] => (item=une_variable) => {
"msg": "UneVariable"
}
ok: [localhost] => (item=une_autre_variable) => {
"msg": "UneAutreVariable"
}

Passer plusieurs variables à un filtre personnalisé

Nous allons ajouter un suffixe à notre filtre. La fonction devient :

def snake_to_camel(text,suffix=''):
import re
return suffix + ''.join(x.title() or '_' for x in text.split('_'))

Et le playbook :

---
- hosts: localhost
gather_facts: false
vars:
text1: "une_variable"
text2: "une_autre_variable"
tasks:
- name: Snake to Camel
ansible.builtin.debug:
msg: "{{ item | snaketocamel('string') }}"
with_items:
- "{{text1}}"
- "{{text2}}"

Ce qui donne comme résultat :

ok: [localhost] => (item=une_variable) => {
"msg": "stringUneVariable"
}
ok: [localhost] => (item=une_autre_variable) => {
"msg": "stringUneAutreVariable"
}

Lever une exception Ansible dans votre filtre

En général, les erreurs rencontrées lors de l’exécution doivent être renvoyées en levant une AnsibleError() ou une classe similaire contenant un message décrivant l’erreur. Lorsque vous encapsulez d’autres exceptions dans des messages d’erreur, vous devez toujours utiliser la fonction Ansible to_text pour garantir la compatibilité des chaînes entre les différentes versions de Python.

Un exemple : Tester qu’une librairie est bien installé : ici la lib six.

# -*- coding: utf-8 -*-
from ansible.errors import AnsibleError
import re
try:
import six
HAS_LIB = True
except ImportError:
HAS_LIB = False
def snake_to_camel(text, suffix=''):
if not HAS_LIB:
raise AnsibleError('You need to install "six" prior to running '
'snaketocamel filter')
import re
return suffix + ''.join(x.title() or '_' for x in text.split('_'))
class FilterModule(object):
def filters(self):
return {
'snaketocamel': snake_to_camel
}

On désinstalle six et on lance le playbook :

Terminal window
pip3 uninstall six -y
ansible-playbook -i localhost test.yml

Ce qui donne :

TASK [Snake to Camel] ***
fatal: [localhost]: FAILED! => {"msg": "You need to install \"six\" prior to running snaketocamel filter"}

Conclusion

Voila je vous laisse pour que vous puissiez développer vos propres filtres et l’intégrer dans vos playbooks.

Pour tester vos développements sur différentes versions d’Ansible et Python, je vous propose de lire ce billet.