Nornir, Junos et Click

Dernière modification le 11 janvier 2020

Même si Ansible est très bien pour faire du Human-driven Automation face à des équipements Juniper, je trouve gratifiant et plus pratique de développer directement en Python. Comme je l’ai déjà expliqué, j’utilise le protocole NETCONF simplifié par le micro framework PyEZ. Je me sers de PyEZ de façon transparente au travers de NAPALM, lui même piloté par le framework Nornir.

Lorsque l’on écrit des scripts, il peut rapidement y en avoir une grande quantité très proches les uns des autres et qui ne font, chacun, qu’une seule fonction.

C’est pour cette raison, que je rajoute le package Python Click qui permet de réaliser des interfaces CLI (Command-Line Interface) enrichie, de manière à limiter le nombre de scripts.

Prenons comme exemple un script dont l’objectif serait de faire des napalm_get et napalm_configure avec un CLI simplifié.

#!/usr/bin/env python3
from nornir import InitNornir
from nornir.plugins.tasks.networking import napalm_get, napalm_configure
from nornir.plugins.tasks.text import template_file
from nornir.plugins.functions.text import print_title, print_result
import click
import sys

# Limit traceback details
sys.tracebacklimit = 0

# Adds -h for help
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])

# Initialize nornir
nr = InitNornir(config_file='config.yaml')

# Configuration function
def junos_configuration(task, template):
    # Transform inventory data to configuration via a template file
    r = task.run(task=template_file,
                 name=template.split('.')[0]+' configuration',
                 template=template,
                 path=f'templates/')
    # Save the compiled configuration into a host variable
    task.host['config'] = r.result
    # Deploy that configuration to the device using NAPALM
    task.run(task=napalm_configure,
             name='Loading extended '+template.split('.')[0]+' Jinja2 template on the device',
             replace=False,
             configuration=task.host['config'])

# Commands definition
@click.group(context_settings=CONTEXT_SETTINGS)
@click.version_option(version='© Gilbert MOÏSIO v1.0.0')
def tool():
    pass
# get command
@tool.command()
@click.option('-g', '--getters',
    default='facts',
    show_default=True,
    help='Use comma separated NAPALM getters, https://napalm.readthedocs.io/en/latest/support/'
)
def get(getters):
    result = nr.run(
        napalm_get,
        getters=getters.split(',')
    )
    print_result(result)
# configure command
@tool.command()
@click.option('-t', '--template',
    help='Jinja2 template file name'
)
def configure(template):
    print_title('Configuring Junos devices')
    result = nr.run(task=junos_configuration, template=template)
    print_result(result)


# Main program
if __name__ == '__main__':
    tool()