Dans la deuxième partie, vous avez créé et utilisé des playbooks et des rôles pour gérer le déploiement automatique de MediaWiki.
Dans ce chapitre, vous allez créer un module Ansible personnalisé. En effet, vous allez créer un module capable de compter le nombre de pages MediaWiki en interrogeant directement la base de données.
Pourquoi écrire un module ?
Il existe déjà beaucoup de modules et la collection s'enrichit régulièrement. Mais dans certain cas, il est nécessaire d’aller plus loin, comme dans votre cas : le module pour compter le nombre de pages dans un MediaWiki n’existe pas. Dans ce cas, il est préférable de faire un module spécifique pour répondre directement à ce besoin.
Dans votre cas, vous allez créer un module qui va exécuter une requête SQL. Cette requête comptera le nombre de pages dans la base MediaWiki.
Pour créer le module, vous aurez besoin de :
écrire un script Python ;
créer un répertoire pour mettre le script ;
tester le module ;
documenter le module.
Comment faire un module ?
Ansible gère automatiquement toutes les étapes de fonctionnement d’un module.
En effet, quand un module est utilisé, Ansible a besoin de faire plusieurs actions, comme aller lire le fichier de configuration pour trouver l'emplacement des modules, ou établir une connexion avec les nodes, ou encore créer un fichier autoporteur et le déposer sur les nodes avant de l'exécuter.
Donc, pour créer un module, Il suffit simplement de mettre votre programme dans le répertoire library et d'écrire un programme Python en utilisant la classe AnsibleModule.
Emplacement du module
Comme indiqué plus haut, vous allez créer un répertoire library au même niveau que les playbooks, c’est-à-dire dans votre environnement de travail.
Dans ce répertoire, vous allez créer votre programme Python, que vous allez appeler count_page.py.
Ansible ira regarder dans ce répertoire lorsque le module count_page sera appelé par le playbook.
Au préalable, connectez-vous sur le node manager avec l’utilisateur user-ansible et activez l’environnement virtuel :
$ source ansible/bin/activate
Puis créez le répertoire library :
$ mkdir library
Écrivez le programme Python
Vous allez écrire le programme qui sera exécuté par le module.
Vous allez structurer votre programme de la façon suivante :
Créer un en-tête.
Définir et gérer les arguments.
Établir une connexion avec la base de données pour exécuter la requête SQL.
Et transmettre les résultats à Ansible.
Éditez le fichier count_page.py :
$ vi library/count_page.py
Créez l'en-tête
L’en-tête du programme indique l'interpréteur à utiliser pour exécuter le programme, et le type d’encodage du fichier. La majorité des programmes Python commencent de cette façon :
#!/usr/bin/python
# -*- coding: utf-8 -*-
Définition des arguments du module
Vous allez utiliser deux arguments pour indiquer au module le nom de la base de données et la requête à utiliser :
db_name
: nom de la base de données ;request
: requête SQL à lancer.
Pour intégrer ces arguments au programme, vous avez besoin de :
importer la classe
AnsibleModule
;définir la fonction
main()
;créer une instance de la classe
AnsibleModule
avec son constructeur ;utiliser le paramètre
argument_spec
pour définir les arguments pris en charge par le module, ainsi que leurs types.
from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(
argument_spec=dict(
db_name = dict(required=True, type=’str’),
request = dict(required=True, type=’str’),
)
)
Récupération de la valeur des arguments
Vous allez maintenant récupérer la valeur des arguments à l’aide des fonctions params et gets de l’instance module, et les placer dans deux variables qui portent le même nom que les arguments.
db_name_local = module.params.get(’db_name’)
request_local = module.params.get(’request’)
Connexion à la base
Pour la connexion à la base, vous avez besoin de :
importer la bibliothèque MySQLdb ;
définir un objet de connexion ;
définir un curseur sur l’objet ;
exécuter une requête sur le curseur ;
stocker le résultat de la requête dans une variable ;
fermer la connexion.
import MySQLdb
db = MySQLdb.connect(db=db_name_local)
cur = db.cursor()
cur.execute(request_local)
results = cur.fetchall()
db.close()
Transmission du résultat à Ansible
La transmission du résultat se fait en utilisant la fonction module.exit_json avec les arguments changed et resultat qui indiquent respectivement l'état de l’action et le résultat de la requête.
Le programme se termine avec l’appel à la fonction main().
module.exit_json(changed=False, resultat=results)
if __name__ == "__main__":
main()
Le code entier du module est donc :
Test du module
Maintenant que tout est au point, vous allez pouvoir tester votre module.
Vous allez créer le playbook qui utilisera le module en question.
$ vi module.yml
À la différence des playbooks précédents qui utilisaient les rôles, celui-ci fait appel à une section tasks, qui contiendra un appel au module count_page :
- name: "Requete dans une base"
indique le nom du jeu d'instructions ;hosts: bdd1
indique le nom du node sur lequel sera exécutée l’action ;gather_facts: no
indique de désactiver les facts ;tasks:
indique que la section contient des tâches :- name: "compte le nombre de pages dans le Wiki"
indique le nom de la tâche :count_page:
indique le module à utiliser,db_name: "mediawiki"
indique le nom de la base de données,request: "select count(*) from page;"
indique la requête à exécuter ,
register: resultat
indique de stocker le résultat de la requête dans la variable request ;
- debug: var=resultat
indique d’afficher le résultat de la requête contenue dans la variable result.
Lancez maintenant le playbook module.yml avec la commande suivante :
$ ansible-playbook -i inventaire.ini module.yml -K -b
Sont utilisées ici les options :
-i
pour l’inventaire à utiliser ;-k
pour le password sudo ;-b
pour passer en sudo.
(ansible) user-ansible@node-manager:~$ ansible-playbook -i inventaire.ini module.yml -K -b BECOME password: PLAY [Requete dans une base] ************************************************************************************* TASK [compte le nombre de pages dans le Wiki] ******************************************************************** ok: [bdd1] TASK [debug] ***************************************************************************************************** ok: [bdd1] => { "resultat": { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "failed": false, "resultat": [ [ 1 ] ] } } PLAY RECAP ******************************************************************************************************* bdd1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Voici le résultat de la requête : il y a 1 pages dans le wiki ! Il va falloir s’y mettre, car pour le moment c’est un peu light !
Gestion de l’aide
Pour agrémenter votre code, vous allez intégrer de la documentation. Ainsi, en utilisant la commande ansible-doc, vous pourrez donner des informations utiles pour l’utilisation de votre module.
Vous allez ajouter 3 déclarations dans votre programme :
DOCUMENTATION qui contiendra le nom du module, la description et les options.
EXAMPLES qui contiendra un exemple pour utiliser correctement votre module.
RETURN qui contiendra la description du résultat retourné par votre module.
Pour vérifier ce que ça donne, vous allez utiliser la commande ansible-doc avec l’option -M pour indiquer le chemin du module, suivi du nom du module :
(ansible) user-ansible@node-manager:~$ ansible-doc -M library count_page > COUNT_PAGE (/home/user-ansible/library/count_page.py) Module qui permet d'exécuter une requête SQL OPTIONS (= is mandatory): = db_name nom de la base de données = request requête à exécuter AUTHOR: Alexandre EXAMPLES: - name: "SQL" count_page: db_name: "BDD" request: "select * from user;" RETURN VALUES: resultat: retourne le résultat de la requête
Ainsi, vous pouvez retrouver de l’information sur le module sans aller voir dans le code.
Vous pouvez maintenant proposer votre module à la communauté et participer au développement d’Ansible. :D
Et voici le fichier library/count_page.py dans son ensemble :
#!/usr/bin/python
from ansible.module_utils.basic import AnsibleModule
import MySQLdb
def main():
module = AnsibleModule(
argument_spec=dict(
db_name = dict(required=True, type='str'),
request = dict(required=True, type='str'),
)
)
db_name_local = module.params.get('db_name')
request_local = module.params.get('request')
db = MySQLdb.connect(db=db_name_local)
cur = db.cursor()
cur.execute(request_local)
results = cur.fetchall()
db.close()
module.exit_json(changed=False, resultat=results)
if __name__ == "__main__":
main()
DOCUMENTATION='''
module: count_page
author: Alexandre
description: Module qui permet d'exécuter une requête SQL
options:
db_name:
description: nom de la base de données
required: yes
request:
description: requête à exécuter
required: yes
'''
EXAMPLES='''
- name: "SQL"
count_page:
db_name: "BDD"
request: "select * from user;"
'''
RETURN = '''
resultat:
description: retourne le résultat de la requête
'''
Résumé
Dans ce chapitre, vous avez créé votre propre module Ansible :
vous avez écrit un programme Python en utilisant la classe AnsibleModule ;
vous avez construit votre programme grâce à plusieurs sections qui vous ont permis de définir des arguments, de vous connecter à la base de données, de lancer une requête SQL et de transmettre le résultat à Ansible ;
vous avez ensuite placé votre programme dans le répertoire library, afin qu’il soit pris en compte par Ansible ;
vous avez ensuite testé votre module à l’aide d’un playbook ;
et pour finir, vous avez agrémenté votre programme avec de la documentation, ce qui permet de savoir comment utiliser votre module.
Un dernier mot
Vous êtes désormais prêt à automatiser toutes vos tâches d’administration. Grâce à Ansible, vous allez gagner du temps et vous pourrez vous concentrer sur des tâches à plus forte valeur ajoutée. Finies les actions chronophages et répétitives ; en écrivant ou en utilisant des playbooks ou des rôles, vous allez gagner en efficacité. Vous avez également créé votre propre module, vous pouvez maintenant voler de vos propres ailes et pourquoi pas, participer à la communauté Ansible.
J’ai été ravi de partager mon expérience avec vous, et j'espère vous croiser prochainement parmi les utilisateurs ou les contributeurs actifs de Ansible. :)