Développement de modules Ansible
Mise à jour :
Au cœur du fonctionnement d’Ansible se trouvent les modules, qui sont les unités fondamentales d’exécution. Ces modules sont des scripts autonomes qui effectuent des actions spécifiques sur les systèmes cibles, comme gérer des packages, des utilisateurs ou des services. La bibliothèque de modules incluse avec Ansible couvre une large gamme de fonctionnalités, mais il arrive que dans certains cas spécifiques, un module personnalisé soit nécessaire.
Développer un module personnalisé permet d’adapter Ansible à des besoins très spécifiques, par exemple pour interagir avec une API propriétaire ou pour gérer des services internes. Ces modules offrent une grande flexibilité et bien qu’Ansible en inclue déjà plusieurs centaines par défaut, créer son propre module donne un contrôle total sur la manière dont certaines actions sont exécutées dans l’infrastructure.
Prérequis pour commencer à développer un module Ansible
Avant de plonger dans le développement de modules Ansible, il est essentiel de maîtriser Python, puis de s’assurer que votre environnement est correctement configuré. Cette section décrit les prérequis et les outils nécessaires pour débuter, de l’installation des composants requis à la configuration d’un environnement de développement adapté.
Installation de Python
Les modules Ansible sont généralement développés en Python, qui est un langage simple et largement utilisé dans la communauté Ansible. Par conséquent, il est indispensable d’avoir Python 3.x installé sur votre machine.
Vous pouvez vérifier si Python est installé en exécutant cette commande dans votre terminal :
Si Python n’est pas installé, vous pouvez aller jetter un œil sur ma documentation Python
Installation d’Ansible
Pour commencer le développement, vous aurez évidemment besoin d’Ansible. Si Ansible n’est pas encore installé, vous pouvez l’installer facilement via pip. Voici la commande pour installer Ansible avec pip :
Pour vérifier que l’installation s’est bien déroulée, exécutez la commande suivante :
Vous pouvez utiliser aussi un environnement virtuel.
Configuration de l’environnement de développement (IDE)
Bien que vous puissiez utiliser n’importe quel éditeur de texte pour développer un module Ansible, il est recommandé d’utiliser un IDE (environnement de développement intégré) qui facilite la gestion du code, offre des fonctionnalités comme l’autocomplétion et la coloration syntaxique et intègre des outils de débogage. Voici quelques IDE populaires que vous pouvez utiliser pour développer des modules Ansible :
- Visual Studio Code (VS Code) : Un IDE open source très populaire, compatible avec de nombreux langages. Vous pouvez y ajouter des extensions Ansible et Python pour améliorer votre expérience de développement. Plus d’infos
- PyCharm : Un IDE puissant pour Python, idéal pour le développement de modules Ansible. PyCharm offre de nombreuses fonctionnalités telles que l’autocomplétion et l’intégration Git.
- Sublime Text : Un éditeur de texte léger avec des fonctionnalités de base, mais efficaces, adapté à ceux qui préfèrent la simplicité.
Quelle que soit votre préférence, assurez-vous d’avoir un environnement avec lequel vous pouvez facilement organiser votre code, gérer des projets Python et effectuer des tests.
Tests et Débogage
Le développement d’un module Ansible nécessite également la capacité de tester et déboguer vos modules. Cela peut se faire en utilisant l’outil ansible-test. Cet outil vous permet d’exécuter des tests sur votre module pour vous assurer qu’il fonctionne correctement dans différents scénarios. Je reviendrai plus en détail sur cet aspect dans une section ultérieure du guide, mais il est bon d’avoir déjà un environnement de test prêt.
Mise en place d’un projet de module Ansible (avec ansible.cfg
)
Une fois que votre environnement est prêt, la première étape consiste à
organiser correctement votre projet pour le développement de votre module
Ansible. Au lieu de placer vos modules dans le répertoire library/
, ce qui
peut poser un problème sur macOS, vous pouvez spécifier un chemin personnalisé
en utilisant un fichier de configuration ansible.cfg
.
Voici les étapes à suivre pour configurer votre environnement de manière efficace en utilisant un répertoire personnalisé pour vos modules.
Créez un répertoire pour vos modules
Dans le répertoire de votre projet Ansible (où se trouve votre playbook), créez
un répertoire spécifique pour vos modules, par exemple custom_modules
. Cela
vous permet de séparer clairement vos modules du reste du projet :
Placer votre module dans le répertoire
Déplacez ou créez votre module personnalisé dans ce répertoire. Par exemple, si
vous développez un module appelé my_custom_module.py
, il devrait être placé
dans le dossier custom_modules
comme ceci :
Configurer ansible.cfg
Dans le même répertoire que votre playbook, créez un fichier ansible.cfg
(ou
modifiez-le si vous en avez déjà un). Ce fichier permettra à Ansible de savoir
où chercher les modules personnalisés.
Voici le contenu à ajouter dans ansible.cfg
pour spécifier le chemin vers
votre répertoire custom_modules
:
Cela dit à Ansible d’ajouter le répertoire custom_modules/
à la liste des
répertoires dans lesquels il recherche les modules.
Structure de votre projet
Votre projet Ansible doit maintenant ressembler à cette structure :
ansible.cfg
: Contient la configuration pour indiquer à Ansible où chercher les modules personnalisés.playbook.yaml
: Votre playbook principal qui utilise le module personnalisé.custom_modules/
: Le dossier où sont stockés vos modules personnalisés.
Les différents blocs composants notre module Ansible
Voici un exemple simple d’un module Ansible personnalisé qui illustre les blocs
essentiels d’un module. Ce module s’appelle my_custom_module
et il retourne un
message de salutation en fonction du nom fourni. Chaque partie sera ensuite
expliquée si besoin.
Le Shebang et le codage UTF-8
Ce bloc indique à l’interpréteur Python comment exécuter le script et utilise l’encodage UTF-8 pour garantir que les caractères spéciaux sont traités correctement.
Le bloc de compatibilité python3
Ces directives assurent la compatibilité avec les anciennes versions de Python (notamment Python 2). Cela garantit que les imports fonctionnent correctement, et que le comportement des divisions et des fonctions soit celui de Python 3, même si le module est exécuté dans un environnement Python 2.
Le bloc Copyright et Licence
Ce bloc décrit le copyright et la licence sous laquelle le module est distribué.
Le bloc DOCUMENTATION
Le bloc de documentation fournit une description du module, les options disponibles, les types de données et les valeurs par défaut, ainsi que des informations sur l’auteur.
Le bloc EXAMPLES
Ce bloc donne des exemples d’utilisation du module dans un playbook Ansible.
Le bloc RETURN
Le bloc RETURN décrit les valeurs renvoyées par le module après son exécution.
Les Imports Python
Ce bloc importe les bibliothèques Python nécessaires pour faire fonctionner le
module. Ici, on utilise AnsibleModule
de la bibliothèque
ansible.module_utils.basic
pour gérer les paramètres et la sortie.
La Fonction principale du module
La fonction principale gère la logique du module. Ici, elle prend en entrée le
paramètre name
et retourne un message de salutation.
- Le module accepte un seul argument
name
, qui est requis. - La fonction
run_module()
définit les arguments, exécute la logique du module et retourne un message personnalisé sous la forme “Hello, {name}”. - Le module ne change pas l’état du système, donc
changed
est défini surFalse
. - Enfin, le module utilise
module.exit_json()
pour renvoyer les résultats à Ansible.
Exécution du playbook
Lorsque vous exécutez votre playbook, Ansible va maintenant automatiquement
chercher les modules dans le répertoire custom_modules
, grâce à la
configuration dans ansible.cfg
. Assurez-vous simplement que votre playbook
fait référence à votre module correctement :
Pour lancer le playbook, exécutez cette commande :
Vous devriez obtenir ce résultat :
Explications sur le bloc DOCUMENTATION
Le bloc DOCUMENTATION dans un module Ansible est essentiel, car il permet de documenter le fonctionnement du module de manière claire et précise. Il suit une structure en YAML et fournit des informations détaillées sur les options, la description, les exemples d’utilisation et l’auteur du module. Ce bloc permet aux utilisateurs de comprendre comment utiliser le module sans avoir besoin de lire le code source.
Structure du bloc DOCUMENTATION
Voici un exemple typique de bloc DOCUMENTATION
:
Voyons plus en détail les différents éléments qui composent ce bloc.
Le champ module
Le champ module
indique le nom du module. Ce nom doit correspondre au
fichier du module sans l’extension .py
. Par exemple, si le module s’appelle
my_custom_module.py
, le nom du module dans la documentation doit être
my_custom_module
.
Le champ short_description
Le champ short_description
donne une brève description du module (généralement
une seule ligne). Cette description est utilisée dans la documentation générée
par Ansible et doit résumer l’objectif principal du module.
Exemple :
Le champ description
Le champ description
fournit une description détaillée du module. Ce champ
peut contenir plusieurs lignes expliquant ce que fait le module, à quoi il sert
et dans quels contextes il peut être utilisé. C’est ici que tu donnes une vue
d’ensemble du fonctionnement du module.
Exemple :
Le champ options
Le champ options
est probablement le plus important, car il décrit les
paramètres (ou arguments) que le module accepte. Chaque option a plusieurs
sous-champs :
- description : Donne une explication sur le rôle de l’option.
- required : Indique si l’option est obligatoire ou facultative (
true
oufalse
). - type : Définit le type de l’option parmi:
str
,int
,bool
,dict
,list
etfloat
.
Exemple d’option :
Dans cet exemple, l’option name
est un paramètre obligatoire (required: true
), qui doit être de type str
(chaîne de caractères).
Comprendre les types dans le bloc options
Lors de la définition des options dans le bloc DOCUMENTATION
d’un module
Ansible, il est important de préciser le type de chaque paramètre que le module
attend. Ces types permettent à Ansible de valider les entrées de l’utilisateur
avant d’exécuter le module. Voici les principaux types que vous pouvez utiliser,
avec des explications et des exemples d’utilisation dans un module Ansible.
str
(chaîne de caractères)
Le type str
représente une chaîne de caractères. Ce type est utilisé
lorsque le module attend du texte en entrée. Cela peut être un nom, un chemin de
fichier, ou n’importe quelle donnée textuelle.
Exemple :
Ici, l’option name
doit être une chaîne de caractères. Un exemple de valeur
valide serait "Alice"
.
int
(entier)
Le type int
représente un nombre entier. Ce type est utilisé pour les
paramètres qui doivent être des nombres sans virgule, comme des ports, des
quantités ou des identifiants.
Exemple :
Ici, l’option timeout
doit être un entier, par exemple 30
, qui représente un
temps en secondes.
bool
(booléen)
Le type bool
(booléen) représente une valeur vraie ou fausse. Ce type est
utilisé lorsque l’option est une simple bascule entre deux états, par exemple
activer ou désactiver une fonctionnalité.
Exemple :
Dans cet exemple, l’option enabled
est un booléen. Elle peut être true
ou
false
, avec true
comme valeur par défaut.
list
(liste)
Le type list
représente une collection d’éléments. Ce type est utilisé
lorsque le module accepte plusieurs valeurs pour une même option. Chaque élément
de la liste peut être de n’importe quel type, selon ce que vous spécifiez dans
le module.
Exemple :
Dans cet exemple, items
doit être une liste de chaînes de caractères. Une
valeur valide serait par exemple ["item1", "item2", "item3"]
.
dict
(dictionnaire)
Le type dict
représente une collection clé-valeur. Ce type est utilisé
lorsque vous avez besoin de structurer plusieurs informations ensemble sous la
forme de paires clé-valeur. Chaque clé doit correspondre à un sous-paramètre.
Exemple :
Dans cet exemple, config
est un dictionnaire qui contient deux sous-options :
path
(une chaîne de caractères) et retries
(un entier).
float
(nombre à virgule flottante)
Le type float
représente un nombre à virgule. Ce type est utilisé lorsque
le paramètre attend un nombre décimal, comme une mesure, une précision, ou un
ratio.
Exemple :
Dans cet exemple, threshold
est un nombre à virgule flottante. Une valeur
possible pourrait être 0.75
, représentant par exemple un seuil de 75%.
Exemples complet
Voici un exemple plus complet d’un bloc DOCUMENTATION
utilisant différents
types pour les options :
Cet exemple montre comment utiliser différents types pour définir les options
d’un module Ansible, avec des options de type chaîne de caractères (str
),
entier (int
), booléen (bool
), liste (list
), dictionnaire (dict
) et
nombre à virgule flottante (float
).
Le champ author
Le champ author
indique le ou les auteurs du module. Il est recommandé
d’inclure le nom et l’adresse e-mail ou le pseudo GitHub de la personne ou des
personnes qui ont créé le module.
Exemple :
Cela permet de donner crédit à l’auteur et de savoir à qui s’adresser en cas de questions ou de contributions.
Formatage des descriptions
Dans les blocs DOCUMENTATION, EXAMPLE et RETURNS des modules Ansible, il n’est
pas possible d’utiliser du Markdown, du reStructuredText (reST), ni des
balises HTML échappées pour formater le texte. Cela signifie que vous ne
pouvez pas utiliser de mise en forme comme les balises HTML (<b>
, <i>
) ou
les syntaxes courantes de Markdown (**gras**
, *italique*
).
À la place, Ansible propose des macros spécifiques qui permettent de formater le texte dans la documentation des modules. Ces macros vous permettent de rendre certains éléments plus lisibles et attrayants, tout en respectant les contraintes de format de la documentation Ansible.
Voici les principales macros disponibles :
- B() - Texte en gras.
- I() - Texte en italique.
- U() - Ajouter un lien direct.
- R() - Ajouter un lien cliquable avec texte.
- M() - Référence à un module Ansible.
- P() - Référence à un plugin Ansible.
- O() - Option d’un module.
- V() - Valeur d’une option.
- RV() - Valeur retournée par un module.
- E() - Variable d’environnement.
- C() - Texte en monospace (code).
Ces macros sont les méthodes standards pour formater la documentation des modules Ansible, assurant ainsi une documentation plus claire, lisible et bien structurée.
Exemple :
Explication sur le bloc EXAMPLES
Le bloc EXAMPLES dans un module Ansible fournit des exemples d’utilisation du module à l’intérieur d’un playbook. Ce bloc est important, car il permet aux utilisateurs de comprendre rapidement comment intégrer et utiliser le module dans leurs propres tâches Ansible. Il doit être simple, clair et fournir plusieurs scénarios d’utilisation typiques du module.
Voici un exemple complet basé sur le module complexe que nous avons vu précédemment. Il montre différents cas d’utilisation du module avec des options de types divers.
Exemple de bloc EXAMPLES
Meilleures pratiques pour le bloc EXAMPLES
- Simples et clairs : Les exemples doivent être faciles à lire et montrer comment utiliser les options de base du module.
- Variés : Inclure des cas d’utilisation basiques ainsi que des scénarios plus complexes avec des combinaisons d’options.
- Réels : Les exemples doivent correspondre à des cas d’utilisation réels et aider les utilisateurs à voir la valeur ajoutée du module.
- YAML valide : Assurez-vous que les exemples sont bien formatés en YAML et peuvent être copiés-collés directement dans un playbook.
Le bloc RETURN
Le bloc RETURN dans un module Ansible décrit les données que le module retourne à la fin de son exécution. Il spécifie les informations que le module renvoie à Ansible et à l’utilisateur, notamment le format et le type des données. Cette section est essentielle pour comprendre quelles valeurs sont disponibles après l’exécution du module.
Structure du bloc RETURN
Voici un exemple de bloc RETURN basé sur le module complexe que nous avons vu précédemment, qui renvoie différents types de données en fonction des options fournies.
Exemple de bloc RETURN
Explication des champs dans le bloc RETURN
-
greet
:- Description : C’est le message de salutation retourné par le module en
fonction de l’option
name
fournie. - Type : Il s’agit d’une chaîne de caractères (
str
). - Returned : Ce champ est toujours retourné, indépendamment des autres options.
- Sample : Exemple de valeur retournée (
'Hello, Alice'
).
- Description : C’est le message de salutation retourné par le module en
fonction de l’option
-
attempts
:- Description : Le nombre de tentatives effectuées avant la fin de l’opération.
- Type : Entier (
int
). - Returned : Ce champ est retourné uniquement si l’option
retries
est définie. - Sample : Exemple de valeur (
5
).
-
items_processed
:- Description : Liste des éléments traités par le module si une liste
d’éléments a été fournie dans l’option
items
. - Type : Liste (
list
) d’éléments de typestr
. - Returned : Ce champ est retourné uniquement lorsque l’option
items
est définie. - Sample : Exemple de valeur (
['item1', 'item2', 'item3']
).
- Description : Liste des éléments traités par le module si une liste
d’éléments a été fournie dans l’option
-
config_used
:- Description : Dictionnaire contenant la configuration utilisée pendant
l’exécution du module, lorsque l’option
config
est fournie. - Type : Dictionnaire (
dict
) avec plusieurs sous-options (path
,timeout
etc.). - Returned : Ce champ est retourné uniquement lorsque l’option
config
est définie. - Sample : Exemple de valeur (
{'path': '/etc/my_config.conf', 'timeout': 60}
).
- Description : Dictionnaire contenant la configuration utilisée pendant
l’exécution du module, lorsque l’option
-
threshold_reached
:- Description : Indique si le seuil critique spécifié par l’option
threshold
a été atteint pendant l’opération. - Type : Booléen (
bool
), soittrue
soitfalse
. - Returned : Ce champ est retourné uniquement lorsque l’option
threshold
est définie. - Sample : Exemple de valeur (
true
).
- Description : Indique si le seuil critique spécifié par l’option
Détails sur les champs du bloc RETURN
- Description : Chaque champ dans le bloc RETURN doit inclure une description expliquant précisément ce que le module retourne.
- Type : Le type de la donnée retournée, qui peut être
str
(chaîne de caractères),int
(entier),list
(liste),dict
(dictionnaire), oubool
(booléen). - Returned : Spécifie les conditions dans lesquelles la donnée est
retournée. Par exemple,
always
signifie que la valeur est toujours retournée, tandis quewhen option_name is defined
signifie que la valeur est retournée uniquement si l’option spécifique a été fournie. - Sample : Fournit un exemple de la donnée retournée afin que l’utilisateur puisse comprendre à quoi s’attendre.
Bonnes pratiques pour le bloc RETURN
- Clarté : Chaque retour doit être bien documenté pour que l’utilisateur sache exactement ce qu’il obtient après l’exécution du module.
- Complet : Tous les retours possibles doivent être couverts, même ceux conditionnels (par exemple, lorsqu’une option spécifique est définie).
- Exemples : Les exemples (
sample
) aident les utilisateurs à comprendre le format des retours et à mieux intégrer les données dans leurs playbooks.
Explications sur la Fonction principale du module
La fonction principale d’un module Ansible est le cœur de la logique du module. C’est ici que le module gère les paramètres fournis, exécute les actions nécessaires et retourne les résultats à Ansible. Cette fonction est indispensable, car elle contrôle le comportement du module en fonction des options définies dans le playbook. Nous allons examiner la structure typique de cette fonction en relation avec le premier exemple de module simple.
Exemple de la fonction principale
Voici l’exemple de notre module simple my_custom_module
:
Structure et éléments de la fonction principale
Le rôle de la fonction run_module()
La fonction run_module()
est la fonction principale de ce module. Elle
contient la logique du module, gère les entrées fournies par l’utilisateur et
renvoie les résultats à Ansible. Voici un décryptage détaillé des différents
composants de cette fonction.
Définition des paramètres : module_args
La première étape de la fonction consiste à définir les paramètres acceptés
par le module. Ces paramètres sont décrits dans un dictionnaire appelé
module_args
. Dans cet exemple, le module attend un seul paramètre, name
, qui
est une chaîne de caractères obligatoire :
name
: Ce paramètre est défini comme une chaîne de caractères (str
) et est obligatoire (required: true
). Cela signifie que si l’utilisateur n’inclut pas le paramètrename
dans son playbook, Ansible renverra une erreur.
Initialisation de l’AnsibleModule
La fonction AnsibleModule
est utilisée pour initialiser le module Ansible
et gérer les paramètres, la validation et les erreurs. Cette fonction prend en
entrée la définition des paramètres (via argument_spec
) et d’autres options
comme la prise en charge du mode de contrôle (supports_check_mode
).
argument_spec
: C’est le dictionnaire des paramètres acceptés par le module, ici défini parmodule_args
.supports_check_mode
: Indique si le module supporte le mode de vérification (check mode), où Ansible exécute une simulation de la tâche sans réellement appliquer de changements. Ici, le module supporte le mode de vérification.
Accès aux paramètres fournis par l’utilisateur
Une fois que le module est initialisé, on peut accéder aux paramètres que
l’utilisateur a fournis via la variable module.params
. Ici, nous récupérons la
valeur du paramètre name
:
Cela permet de récupérer le nom que l’utilisateur a passé dans le playbook et de l’utiliser dans la suite du traitement du module.
Logique principale du module
La logique principale du module repose sur le traitement du paramètre name
pour générer un message de salutation. Le module ne modifie pas le système, donc
la valeur changed
est définie à False
(indiquant qu’aucun changement n’a été
fait) :
changed: False
: Comme ce module ne modifie aucun état du système, on indique qu’aucun changement n’a été effectué.greet
: Le message personnalisé est généré à partir de la valeur dename
et le résultat est stocké dans le dictionnaireresult
.
Retour des résultats : module.exit_json()
Une fois la logique terminée, le module doit retourner les résultats à
Ansible. Cela se fait avec la fonction module.exit_json()
, qui envoie les
résultats au système Ansible sous forme de JSON. Le contenu du dictionnaire
result
est passé à cette fonction :
Ici, nous passons le dictionnaire result
avec changed=False
et le message de
salutation sous la clé greet
.
Conclusion
Dans ce guide, nous avons parcouru les étapes essentielles pour développer un
module Ansible personnalisé, de la structure de base du module à l’écriture
des différents blocs comme DOCUMENTATION
, EXAMPLES
et RETURN
. En suivant
ces bonnes pratiques, vous êtes maintenant capable de créer des modules robustes
et bien documentés qui répondent à des besoins spécifiques.
La suite naturelle de ce développement consiste à organiser et distribuer vos modules au sein de collections Ansible. Les collections permettent de regrouper des modules, des rôles, des plugins et des playbooks dans un ensemble cohérent et réutilisable, facilitant ainsi leur partage et leur utilisation au sein de grandes infrastructures. Dans le prochain chapitre, nous aborderons en détail la manière de créer et structurer une collection Ansible et comment y intégrer vos modules personnalisés pour une distribution plus flexible et modulaire.
Le développement de collections est une étape clé pour ceux qui souhaitent créer des modules réutilisables et maintenables à grande échelle, tout en assurant une meilleure organisation et gestion des dépendances dans leurs projets Ansible.