Avant les vacances (tout au moins, les miennes), nous avons vu comment utiliser les templates et le langage de description Jinja2, regardons à présent comment les utiliser dans Ansible.

Tout est Jinja

Dans les playbooks Ansible, toutes les données sont implicitement converties par l’interpréteur Jinja2.

Par exemple, notre playbook show ip interface brief peut être écrit de la façon suivante :

---
# show ip interface brief
- hosts: all
  gather_facts: no
  connection: network_cli
  vars:
      # playbook data
      iib: ip interface brief
      iib_filter: DHCP
      register_var: output
      name: show ip int brief en mode cryptique
      params: command
      # inventory data
      ansible_network_os: ios
      ansible_user: cisco
      ansible_ssh_pass: cisco

  tasks:
  - name: "{{ name }}"
    cli_command:
        command: show {{ iib }} | ex {{ iib_filter }}
    register: "{{ register_var }}"

  - name: debug output
    debug:
       var: output

Bien sûr, je ne vous encourage pas à écrire des playbooks de ce type, néanmoins il est parfaitement fonctionnel. Et il faut garder en tête à cette possibilité très simple à mettre en œuvre pour passer des paramètres à une commande (recherche d’une adresse MAC, d’une route, …) ou, comme nous allons le voir, s’assurer de l’unicité d’une ressource.

le module template

Comme attendu le module template permet de croiser un template texte avec des variables. Nous l’utiliserons pour deux principaux usages :

  • Créer un fichier de configuration adapté à chaque équipement (création de service)
  • Fournir un rapport particularisé d’une commande opérationnelle (supervision)

En exemple, nous cherchons à configurer en masse (ie: sur nos 2 routeurs de lab !) une adresse IP sur l’interface LAN du routeur. L’adresse IP et le nom de l’interface sont des variables du template.

Le template s’écrit assez simplement :

interface GigabitEthernet3
  ip address  {{ rtr.ip_lan }} {{ rtr.mask_lan }}
end

Les données (structurées) sont lues directement dans l’inventaire :

[mon_reseau]
rtr231 ansible_host=10.0.0.231 rtr="{ 'ip_lan': '192.168.231.1', 'mask_lan': '255.255.255.0' }"
rtr232 ansible_host=10.0.0.232 rtr="{ 'ip_lan': '192.168.232.1', 'mask_lan': '255.255.255.0' }"

Pour appeler le module template, il faut passer en paramètres le fichier template (src) et le fichier destination (dest). Comme plusieurs sessions seront lancées en parallèle, nous pensons suffixer le fichier destination par le nom du routeur, pour éviter qu’il ne soit écrasé, d’où le playbook :

---
# Configure LAN interface
- hosts: all
  gather_facts: no
  vars:
       ansible_user: cisco
       ansible_ssh_pass: cisco
       ansible_network_os: ios
  tasks:
  - name: call template
    template:
        src: create_itf.j2
        dest: create_itf.{{inventory_hostname}}.confg

Et ça ne marche pas. La faute au paramètre connection qui essaie, une fois de plus, de générer le template sur le router et se plaint d’avoir un peu de mal à y trouver un interpréteur Jinja2.

La correction consiste à forcer le paramètre connection à local. Le playbook corrigé s’écrit donc :

---
# Configure  LAN interface
- hosts: all
  gather_facts: no
  connection: local
  vars:
       ansible_user: cisco
       ansible_ssh_pass: cisco
       ansible_network_os: ios
       
  tasks:
  - name: call template
    template:
        src: create_itf.j2
        dest: create_itf.{{ inventory_hostname }}.confg

Et cette fois c’est bon, et notre template est interprété comme attendu :

Du coup, nous savons générer des fichiers équipements par équipements à partir du template. Ces fichiers ont été créés sur le serveur Ansible, et il reste à les envoyer sur les routeur. Il existe plusieurs modules qui peuvent s’en charger (raw, net_up, copy, …), ici nous utilisons cli_command pour aller chercher le fichier de configuration sur le serveur Ansible en TFTP.

Le playbook complet devient donc :

---
# Configure  LAN interface
- hosts: all
  gather_facts: no
  vars:
       ansible_user: cisco
       ansible_ssh_pass: cisco
       ansible_network_os: ios
       ansible_command_timeout: 60

  tasks:
  - name: call template
    connection: local
    template:
        src: create_itf.j2
        dest: create_itf.{{ inventory_hostname }}.confg

  - name: activate configuration on router
    connection: network_cli
    cli_command:
        command: copy tftp://10.0.0.110/cfgs/create_itf.{{ inventory_hostname }}.confg running-config
        prompt: Destination filename
        answer: ''
Playbook result

Et voilà comment changer facilement la configuration sur un parc de routeurs !

Playbook result

Conclusion

Mon tour d’horizon est maintenant terminé et vous avez maintenant un aperçu du rôle d’Ansible dans la chaîne d’automatisation.

Maintenant, c’est à vous d’adapter Ansible à vos cas d’usages. Et pour vous lancer, il est raisonnable de démarrer par des projets simples et nécessitant peu de données, puis passer à des tâches plus complexes, toujours avec des données simples, pour terminer par les projets les plus ambitieux, ceux dont la structure de données doit être étudiée consciencieusement avant même de développer l’outil.

En bref, voir large, commencer petit !

Author

Philippe est architecte réseau chez un opérateur depuis 20 ans. Il a le double rôle de concevoir des réseaux pour les clients, puis de les faire fonctionner. Bien que passionné par l'innovation, il reste un fervent supporter de la RFC 1925 et garde tout son sens critique par rapport au hype et aux promesses féeriques des constructeurs. Ancien développeur, il tente de garder la main en programmant des Arduino en C ou des utilitaires opensource en Ruby. On peut également le croiser en randonnée dans les collines ou dans un club de bridge.

Write A Comment