Zwindler sur GithubAllez faire un tour sur mes dépôts Git !

Récupérer les informations sur la distribution avec les facts Ansible

Les facts d’Ansible

Si vous suivez le blog, vous savez que j’utilise énormément Ansible. J’ai même été faire un talk sur le sujet à BDX I/O 2018, à l’ENSEIRB.

Aujourd’hui, plutôt que de vous donner un playbook tout fait pour installer sans effort une des multiples applications dont j’ai automatisé le déploiement avec Ansible, je vous propose d’explorer une des features les plus importantes et utiles d’Ansible, les facts, par le biais d’un petit exemple.

C’est quoi les facts dans Ansible

Si vous avez déjà lancé un playbook, vous avez sûrement remarqué lors de son exécution, que la première tâche qui est réalisée n’est pas une tâche que vous avez demandée explicitement.

ansible-playbook -i inventory/prod/blop -u blop --private-key=blop.key configure-blop.yml
[...]]
TASK [Gathering Facts] *********************************************************
ok: [blop-vm1]

Cette tâche [Gathering Facts], qui ne fait à première vue rien, correspond en fait à la connexion à la ou les machines sur lesquelles seront exécutées les playbooks. Si la connexion échoue, le playbook échouera sur cette cible, et continuera éventuellement sur les autres (s’il en reste). Si l’hôte répond, alors Ansible en profite pour récupérer moult informations en rapport avec cette machines et les stockent dans une variable ansible_facts.

A quoi ça ressemble ?

Comme je ne disais, à première vue, cette commande de fait rien. Si on ne sait pas qu’elle stocke des informations dans une variables, on ne peut rien en faire.

Un bon moyen d’avoir un premier aperçu de ce qu’on peut en faire est de les afficher. On peut faire ça avec le module « setup », qui est en réalité le même module qui est appelé lors de l’étape [Gathering facts].

Le retour se fera au format JSON, ce qui est certes peu digeste, mais facilitera grandement les manipulations de type « filtre » (via le flag filter intégré ou via l’utilitaire jq par exemple).

ansible blop-vm1 i inventory/prod/blop -m setup
blop-vm1 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "10.1.1.10"
        ], 
        "ansible_apparmor": {
            "status": "enabled"
        }, 
[...]

Je ne vous copie-colle pas tout, rien que pour cette machine, j’ai 719 lignes… La quantité d’informations disponible est impressionnante, et on peut même en venir à se demander à quoi ça va bien pouvoir nous servir.

Ok, on en fait quoi ?

Du coup je profite de cet article pour faire un tuto tout simple et qui peut être utile dans de nombreux cas, réaliser des opérations différentes en fonction de la distribution de la machine concernée.

L’information qui va nous intéresser ici concerne donc les remontées en tant que « nom » ou « version » d’une distribution. Je vais vous économiser la lecture de nos 700+ lignes, et vous indiquer que vous pouvez trouver ces informations en filtrant sur les mots clés « ansible_distribution… » et « ansible_os… »

Voilà ce qu’on pourrait obtenir sur une machine CentOS :

ansible blop-vm1 -m setup -a 'filter=ansible_distribution*'
blop-vm1 | SUCCESS => {
  "ansible_facts": {
    "ansible_distribution": "CentOS",
    "ansible_distribution_major_version": "7",
    "ansible_distribution_release": "Core",
    "ansible_distribution_version": "7.2.1511"
  },
  "changed": false
}

ansible blop-vm1 -m setup -a 'filter=ansible_os_family'
blop-vm1 | SUCCESS => {
  "ansible_facts": {
    "ansible_os_family": "RedHat"
  },
  "changed": false
}

Et sur une machine Ubuntu :

ansible blop-vm2 -m setup -a 'filter=ansible_distribution*'
ansible blop-vm2 | SUCCESS => {
    "ansible_facts": {
        "ansible_distribution": "Ubuntu", 
        "ansible_distribution_file_parsed": true, 
        "ansible_distribution_file_path": "/etc/os-release", 
        "ansible_distribution_file_variety": "Debian", 
        "ansible_distribution_major_version": "16", 
        "ansible_distribution_release": "xenial", 
        "ansible_distribution_version": "16.04"
    }, 
    "changed": false
}
ansible blop-vm2 -m setup -a 'filter=ansible_os*'
blop-vm2 | SUCCESS => {
    "ansible_facts": {
        "ansible_os_family": "Debian"
    }, 
    "changed": false
}

Utiliser les facts dans un playbook

Pour continuer dans l’exemple, on va faire une chose qu’il ne faut jamais faire : désactiver le firewall et SELinux.

Imaginons que vous souhaitiez quand même le faire. Si vous lancez ce playbook sur tout votre parc et qu’il n’est pas homogène, vous allez tomber sur des groupes de machines Ubuntu, CentOS 5, 6, 7…

Un moyen de gérer les cas particuliers pourrait être de les classer tous vos hosts dans des groupes, et de créer un playbook pour chaque OS/version, restreint à un groupe de machines uniquement.

---
- hosts: rhelcentos6only
  tasks:
[…]

Ce n’est d’ailleurs pas une mauvaise idée, surtout pour gérer les distrib aussi différentes que RHEL et Ubuntu par exemple.

En revanche, dans le cas de RHEL, on a des différences qui apparaissent à partir de la RHEL 7, notamment la gestion du Firewall qui passe de IPtables à firewalld.

On va donc faire appel à la clause when: de ansible, qui conditionne l’exécution d’une tâche (ou d’un rôle ou d’un block) à une validation. Et ça donnerait quelque chose comme ça :

---
- hosts: rhelcentos6only
  tasks:
  - name: "shutdown and disable IPtables for RHEL <= 6"
    service: 
      name=iptables
      state=stopped
      enabled=no
    when: (ansible_distribution == "CentOS" or ansible_distribution == "RedHat") and
          (ansible_distribution_major_version <= "6") 

  - name: "shutdown and disable firewalld for RHEL >= 7"
    service:
      name=firewalld
      state=stopped
      enabled=no
    when: (ansible_distribution == "CentOS" or ansible_distribution == "RedHat") and
          (ansible_distribution_major_version >= "7")

  - name: "disable selinux"
    selinux:
     state=disabled

Dans cet exemple, lors de l’exécution du playbook, on aura donc, pour chaque VM CentOS ou RHEL, une tâche qui sera exécutée, et l’autre qui sera marquée « skipping » (en bleu clair) et le playbook marchera pour toutes les RHEL, quelque soit leur version.

Aller plus loin

Il existe une page spécifique sur la documentation d’Ansible pour parler des conditions dans les playbooks, accessible à l’adresse suivante : https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html

Vous pouvez aussi utiliser les facts dans les options des tasks, et dans les templates, en encadrant le nom de la variable souhaitée avec des « doubles curly braces » : « {{}} »

- command: "echo {{ ma_super_variable }}"

Sources

One Comment

Add a Comment

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.