Aller au contenu principal

Ansible et les assertions

· 4 minutes de lecture
Stéphane ROBERT
Consultant DevOps

Au début du lancement de l'exécution d'un playbook Ansible, il peut parfois être nécessaire de réaliser des contrôles de conformité avant d'exécuter d'autres actions. Un des premiers contrôles est de vérifier la conformité des variables transmis. C'est là qu'interviennent les modules assert et validate_argument_spec.

Contrôle des variables

Utilisation du module assert

Le module ansible assert permet de vérifier si des expressions sont vrais.

---
- name: Playbook de test
  hosts: all
  become: true
  gather_facts: true
  tasks:
    - name: Check my_param
      ansible.builtin.assert:
        that:
          - my_param <= 100
          - my_param >= 0
        fail_msg: "'my_param' must be between 0 and 100"
        success_msg: "'my_param' is between 0 and 100"

Pour des cas simples assertions peut faire l'affaire. Pour valider des structures plus complexes, nous allons utiliser le module validate_argument_spec.

Utilisation du module validate_argument_spec

Introduit avec la version 2.11 d'Ansible, le module validate_argument_spec est à la base utilisée pour valider des variables de rôles. Nous allons voir qu'il est possible de s'en servir dans un playbook :

---
# Description
- name: Description of the playbook
  hosts: all
  gather_facts: true
  tasks:
    - name: Verify vars needed for this task file are present when included
      ansible.builtin.validate_argument_spec:
        argument_spec: '{{required_data}}'
      vars:
        required_data:
          stuff:
            description: stuff
            type: str
            choices: ['who', 'knows', 'what']
            default: what
          but:
            description: test
            type: str
            required: true

Ici, nous définissons deux valeurs : stuff et but. stuff est une string pouvant prendre les valeurs : who, knows, what.

Si nous lançons son exécution en utilisant des extra vars avec stuff = 'toto'.

ansible-playbook -i localhost, -c local playbook.yml -e but=test -e stuff=toto

...

TASK [Verify vars needed for this task file are present when included] *********
fatal: [localhost]: FAILED! => changed=false
  argument_errors:
  - 'value of stuff must be one of: who, knows, what, got: toto'
  argument_spec_data:
    but:
      description: test
      required: true
      type: str
    stuff:
      choices:
      - who
      - knows
      - what
      default: what
      description: stuff
      type: str
  msg: |-
    Validation of arguments failed:
    value of stuff must be one of: who, knows, what, got: toto
  validate_args_context: {}

PLAY RECAP **********

Pour un exemple de validation de dictionnaire, reportez-vous au billet sur l'écriture des rôles.

Des contrôles plus poussés

Inutile de lancer un playbook complet pour le voir échouer à cause d'un bête manque de place sur un filesystem non ?

Il est tout à fait possible de lancer des contrôles sur la machine sans utiliser le module command. Je vais prendre comme exemple un check de l'espace disque du filesystem root :

---
- name: Playbook de test
  hosts: all
  become: true
  gather_facts: true
  vars:
    max_rate_occupation_disk: 0.8
  tasks:
    - name: Check / usage
    ansible.builtin.assert:
        that:
        - item.size_available > item.size_total|float * (1 - max_rate_occupation_disk/100)
    when: item.mount == "/"
    with_items:
        - "{{ ansible_mounts }}"

Une boucle with_items couplée à une condition when permet de limiter le champ de test au seul filesystem root. Ici j'utilise les facts ansible qui contient toutes sortes d'informations dont les filesystems. Bien sur, il ne faut pas avoir désactivé la collecte de ces facts au démarrage du playbook !

Une autre solution pour limiter la collecte des facts au strict minimum est le désactiver et faire appel ensuite appel au module setup en limitant la collecte avec le paramètre gather_subset :

---
- name: Playbook de test
  hosts: all
  become: true
  gather_facts: false
  tasks:
    - name: collect facts
        ansbile.builtin.setup:
            gather_subset: !all,!min
    - name: Check / usage
        ansible.builtin.assert:
            that:
            - item.size_available > item.size_total|float * (1 - my_disk_slash_space_max/100)
        when: item.mount == "/"
        with_items:
            - "{{ ansible_mounts }}"

Point important, pour éviter de recourir systématiquement au module commande, je vous conseille d'utiliser les custom_facts que je vous ai exposé dans un précédent billet. En effet, il est possible d'utiliser un langage plus évolué que du bash pour les créer. On peut tout imaginer !