Écriture de module avec Ansible
Depuis quelques temps, je m'étais mis en tête de vouloir tester l'espace disque disponible. En effet, il m'arrive assez régulièrement d'avoir un bon gros message d'erreur plus d'espace disponible alors que je viens de faire la moitié de l'installation.
Partant de ce constat, je me suis dit qu'il vaudrait mieux avoir une erreur franche en début d'exécution plutôt qu'au milieu de mon installation (sachant que généralement je peux avoir un truc à moitié bancal que je dois supprimé pour avoir une installation correcte). J'ai donc cherché un moyen de tester la quantité de disque restant pour un emplacement donné évitant ainsi ce type de désagrément.
Petit problème, je n'ai rien trouvé me permettant de faire ce test simplement. N'étant pas non plus emballé de faire ça avec un bout de shell, j'ai donc cherché à écrire le module Ansible qui me permettrait de gérer ce test.
Fichier playbook
Prenons un playbook check_tmp.yml qui va s'assurer que j'ai au moins 40 Mo d'espace disque dans /tmp. Pour se faire, il va faire appel à un module space_available :
--- # Playbook de test - name: "Test" hosts: "localhost" gather_facts: no tasks: - space_available: path=/tmp size=40M
Fichier du module (library/space_available)
Dans le même répertoire que le fichier check_tmp.yml, créer un répertoire library. Dans ce répertoire, il nous suffit de créer un fichier space_available que nous allons alimenter avec le contenu suivant :#!/usr/bin/python # -*- coding: utf-8 -*- # # Author: Yannig Perre# # Check for available space on a given path. # # Documentation DOCUMENTATION = ''' --- version_added: "1.8" module: space_available short_description: space_available description: - This module check if there's enough space available options: path: description: path to check size: description: size needed. notes: requirements: [] author: Yannig Perre ''' EXAMPLES = ''' - name: "40M space available on /tmp" space_available: path=/tmp size=40M ''' # static hash map converter size_convertion = { "k": 1024, "m": 1024*1024, "g": 1024*1024*1024, "t": 1024*1024*1024*1024, "p": 1024*1024*1024*1024*1024 } # Here we go... def main(): # module declaration module = AnsibleModule( argument_spec=dict( path=dict(required=True), size=dict(required=True), ), supports_check_mode=True ) # Retrieving parameters value path = module.params['path'] size = module.params['size'] # Retrieve size (code part stolen from lvol module) if size[-1].isalpha(): if size[-1].lower() in 'kmgtpe': size_unit = size[-1] if not size[0:-1].isdigit(): module.fail_json(msg="Bad size specification for unit %s" % size_unit) size_opt = size[-1] size = int(size[0:-1]) elif not size.isdigit(): module.fail_json(msg="Bad size specification") else: size = int(size) size_opt = 'm' # Convert everything in bytes please unit_size = size_convertion[size_opt.lower()] size = size * unit_size # Try to get ... try: # ... size available statvfs = os.statvfs(path) except OSError, e: # if we fail dying with exception message module.fail_json(msg="%s" % e) space_available = statvfs.f_bavail * statvfs.f_frsize # If not enough room => fail if space_available < size: module.fail_json(msg="Not enough space avaible on %s (found = %0.2f%s, needed = %0.2f%s)" % (path, space_available / unit_size, size_opt, size / unit_size, size_opt)) module.exit_json(msg=path) # Import Ansible Utilities from ansible.module_utils.basic import * main()
Premier test
Lançons maintenant notre playbook avec la commande suivante :ansible-playbook space_available.yml PLAY [Test] ***************************************************** TASK: [space_available path=/tmp size=40M] ********************** ok: [localhost] PLAY RECAP ****************************************************** localhost : ok=1 changed=0 unreachable=0 failed=0
\\o/ Youpi ! Il semblerait que mon poste dispose de plus de 40 Mo de disque dans /tmp !
Quelques tests supplémentaires
Changeons maintenant notre playbook pour voir le comportement de notre module :--- # Playbook de test - name: "Test" hosts: "localhost" gather_facts: no tags: "poste-packaging" tasks: - space_available: path=/does/not/exist size=400M ignore_errors: yes - space_available: path=/root/.aptitude size=400M ignore_errors: yes - space_available: path=/tmp size=400M - space_available: path=/tmp size=1G - space_available: path=/tmp size=37G ignore_errors: yes - space_available: path=/tmp size=40M - space_available: path=/ size=3000M
Lançons notre playbook :
ansible-playbook space_available.yml PLAY [Test] **************************************************** TASK: [space_available path=/does/not/exist size=400M] ********* failed: [localhost] => {"failed": true} msg: [Errno 2] No such file or directory: '/does/not/exist' ...ignoring TASK: [space_available path=/root/.aptitude size=400M] ********* failed: [localhost] => {"failed": true} msg: [Errno 13] Permission denied: '/root/.aptitude' ...ignoring TASK: [space_available path=/tmp size=400M] ******************** ok: [localhost] TASK: [space_available path=/tmp size=1G] ********************** ok: [localhost] TASK: [space_available path=/tmp size=37G] ********************* failed: [localhost] => {"failed": true} msg: Not enough space avaible on /tmp (found = 9.00G, needed = 37.00G) ...ignoring TASK: [space_available path=/tmp size=40M] ********************* ok: [localhost] TASK: [space_available path=/ size=3000M] ********************** ok: [localhost] PLAY RECAP ***************************************************** localhost : ok=7 changed=0 unreachable=0 failed=0
Nous avons bien des indications sur :
- L'absence d'un fichier sur la machine ;
- Un manque de permission ;
- Et enfin, un manque de place.
A noter que les instructions en erreur sont passées du fait de la présence du mot clé ignore_errors: yes.
Pour aller plus loin
Pour plus de détail sur le fonctionnement des modules, je vous invite à vous reporter à la documentation sur le sujet présente sur ansible.com (http://docs.ansible.com/developing_modules.html).