NAPALM Cisco Devices
Network Programmability and AutomationPythonTutoriales

NAPALM Network Automation Python: Trabajando con Cisco IOS y IOS-XR

Introducción

En este artículo vamos conocer las funciones y métodos de NAPALM para colectar datos en routers Cisco IOS y IOS-XR. Escribiremos múltiples scripts en Python para colectar datos de una red, que podrás usar para crear reportes o investigar sobre algún problema. Este es una continuación del artículo en el que introdujimos a NAPALM y es parte de la aventura en la que quiero enseñarles como puedes implementar Programabilidad y Automatización de Redes con Python y la librería NAPALM.

El contenido principal está en el siguiente video. Así que empieza por aquí para que puedas entender algunas secciones del artículo.

NAPALM CLI

La instalación de NAPALM viene con una herramienta que nos permite utilizar NAPALM directamente desde la línea de comandos. Su uso es bastante simple y podemos ver como se utiliza en el menú de ayuda del comando. Ejecutamos el siguiente comando para ver este menú:

napalm --help

Salida del comando:

(napalm_practice) malvarez@Coding-Networks:~$ napalm --help
usage: napalm [-h] [--user USER] [--password PASSWORD] --vendor VENDOR
              [--optional_args OPTIONAL_ARGS] [--debug]
              hostname {configure,call,validate} ...

Command line tool to handle configuration on devices using NAPALM.The script
will print the diff on the screen

positional arguments:
  hostname              Host where you want to deploy the configuration.

optional arguments:
  -h, --help            show this help message and exit
  --user USER, -u USER  User for authenticating to the host. Default: user
                        running the script.
  --password PASSWORD, -p PASSWORD
                        Password for authenticating to the host.If you do not
                        provide a password in the CLI you will be prompted.
  --vendor VENDOR, -v VENDOR
                        Host Operating System.
  --optional_args OPTIONAL_ARGS, -o OPTIONAL_ARGS
                        String with comma separated key=value pairs passed via
                        optional_args to the driver.
  --debug               Enables debug mode; more verbosity.

actions:
  {configure,call,validate}
    configure           Perform a configuration operation
    call                Call a napalm method
    validate            Validate configuration/state

Automate all the things!!!

Desde esta herramienta es posible ejecutar operaciones de configuración a los dispositivos, validar configuraciones o estados y realizar llamadas a los métodos de NAPALM para obtener datos de los dispositivos.

Estas acciones se realizan pasando al comando los argumentos del usuario y la contraseña del dispositivo al que nos vamos a conectar, el sistema operativo, la IP o nombre del equipo y finalmente el nombre del método.

Veamos un ejemplo:

napalm --user codingnetworks --password Coding.Networks1 --vendor ios IOS-R2 call get_interfaces

Salida:

{
    "GigabitEthernet0/0": {
        "is_enabled": true,
        "is_up": true,
        "description": "to_IOS-SW2 Gi0/1",
        "mac_address": "50:00:00:01:00:00",
        "last_flapped": -1.0,
        "mtu": 1500,
        "speed": 1000
    },
    "GigabitEthernet0/0.30": {
        "is_enabled": true,
        "is_up": true,
        "description": "to_IOS-SW2 Gi0/1 Vlan 30",
        "mac_address": "50:00:00:01:00:00",
        "last_flapped": -1.0,
        "mtu": 1500,
        "speed": 1000
    },
    "GigabitEthernet0/1": {
        "is_enabled": false,
        "is_up": false,
        "description": "",
        "mac_address": "50:00:00:01:00:01",
        "last_flapped": -1.0,
        "mtu": 1500,
        "speed": 1000
    },

NAPALM Python

Es en esta sección es donde empezaremos a ver y disfrutar de las bondades de utilizar NAPALM para programabilidad y automatización de redes.

NAPALM Python Tabla de Informaciones

Vamos a escribir un script que se conecte a todos los dispositivos de nuestra red y nos imprima en una tabla informaciones como el hostname, fabricante, modelo, tiempo que tienen en operación y el número de serial de cada uno. A continuación el código:

import napalm
from tabulate import tabulate
def main():
    driver_ios = napalm.get_network_driver("ios")
    driver_iosxr = napalm.get_network_driver("iosxr")
    device_list = [["ios-sw2","ios", "switch"],["ios-r2", "ios", "router"],
    ["ios-r3", "ios", "router"],["iosxr-r1", "iosxr", "router"],["ios-sw1", "ios", "switch"]]
    network_devices = []
    for device in device_list:
        if device[1] == "ios":
            network_devices.append(
                            driver_ios(
                            hostname = device[0],
                            username = "codingnetworks",
                            password = "Coding.Networks1"
                            )
                              )
        elif device[1] == "iosxr":
            network_devices.append(
                            driver_iosxr(
                            hostname = device[0],
                            username = "codingnetworks",
                            password = "Coding.Networks1"
                            )
                              )
    devices_table = [["hostname", "vendor", "model", "uptime", "serial_number"]]
    for device in network_devices:
        print("Connecting to {} ...".format(device.hostname))
        device.open()
        print("Getting device facts")
        device_facts = device.get_facts()
        devices_table.append([device_facts["hostname"],
                              device_facts["vendor"],
                              device_facts["model"],
                              device_facts["uptime"],
                              device_facts["serial_number"]
                              ])
        device.close()
        print("Done.")
    print(tabulate(devices_table, headers="firstrow"))
if __name__ == '__main__':
    main()

En el video se encuentra la explicación de este código.

Veamos la salida del Script:

Connecting to ios-sw2 ...
Getting device facts
Done.
Connecting to ios-r2 ...
Getting device facts
Done.
Connecting to ios-r3 ...
Getting device facts
Done.
Connecting to iosxr-r1 ...
Getting device facts
'list' object has no attribute 'xpath'
'list' object has no attribute 'xpath'
'list' object has no attribute 'xpath'
Done.
Connecting to ios-sw1 ...
Getting device facts
Done.
hostname    vendor    model      uptime  serial_number
----------  --------  -------  --------  ---------------------
IOS-SW2     Cisco     IOSv        34020  9162JOW336J
IOS-R2      Cisco     IOSv        34020  9MJNBPR604WW2WN1QPABR
IOS-R3      Cisco     IOSv        34080  9BO8LXD6KLX8ZSB7M0PPF
IOSXR-R1    Cisco                 34146
IOS-SW1     Cisco     IOSv        34080  9CGHIOOXX08

NAPALM Python Tabla de Interfaces

Vamos a modificar el código anterior para agregar otra funcionalidad. Esta funcionalidad permitirá que el script nos imprima una tabla con todas las interfaces de cada equipo con informaciones de cada una como su estado, descripción, mtu, entre otras. A continuación el código:

import napalm
from tabulate import tabulate
def main():
    driver_ios = napalm.get_network_driver("ios")
    driver_iosxr = napalm.get_network_driver("iosxr")
    device_list = [["ios-sw2","ios", "switch"],["ios-r2", "ios", "router"],
    ["ios-r3", "ios", "router"],["iosxr-r1", "iosxr", "router"],["ios-sw1", "ios", "switch"]]
    network_devices = []
    for device in device_list:
        if device[1] == "ios":
            network_devices.append(
                            driver_ios(
                            hostname = device[0],
                            username = "codingnetworks",
                            password = "Coding.Networks1"
                            )
                              )
        elif device[1] == "iosxr":
            network_devices.append(
                            driver_iosxr(
                            hostname = device[0],
                            username = "codingnetworks",
                            password = "Coding.Networks1"
                            )
                              )
    devices_table = [["hostname", "vendor", "model", "uptime", "serial_number"]]
    devices_table_int = [["hostname","interface","is_up", "is_enabled", "description", "speed", "mtu", "mac_address"]]
    for device in network_devices:
        print("Connecting to {} ...".format(device.hostname))
        device.open()
        print("Getting device facts")
        device_facts = device.get_facts()
        devices_table.append([device_facts["hostname"],
                              device_facts["vendor"],
                              device_facts["model"],
                              device_facts["uptime"],
                              device_facts["serial_number"]
                              ])
        print("Getting device interfaces")
        device_interfaces = device.get_interfaces()
        for interface in device_interfaces:
            devices_table_int.append([device_facts["hostname"],
                                  interface,
                                  device_interfaces[interface]['is_up'],
                                  device_interfaces[interface]['is_enabled'],
                                  device_interfaces[interface]['description'],
                                  device_interfaces[interface]['speed'],
                                  device_interfaces[interface]['mtu'],
                                  device_interfaces[interface]['mac_address']
        ])
        device.close()
        print("Done.")
    print(tabulate(devices_table, headers="firstrow"))
    print()
    print(tabulate(devices_table_int, headers="firstrow"))
if __name__ == '__main__':
    main()

En el video se encuentra la explicación de este código.

Veamos la salida de este código:

Connecting to ios-sw2 ...
Getting device facts
Getting device interfaces
Done.
Connecting to ios-r2 ...
Getting device facts
Getting device interfaces
Done.
Connecting to ios-r3 ...
Getting device facts
Getting device interfaces
Done.
Connecting to iosxr-r1 ...
Getting device facts
'list' object has no attribute 'xpath'
'list' object has no attribute 'xpath'
'list' object has no attribute 'xpath'
Getting device interfaces
Done.
Connecting to ios-sw1 ...
Getting device facts
Getting device interfaces
Done.
hostname    vendor    model      uptime  serial_number
----------  --------  -------  --------  ---------------------
IOS-SW2     Cisco     IOSv        34080  9162JOW336J
IOS-R2      Cisco     IOSv        34140  9MJNBPR604WW2WN1QPABR
IOS-R3      Cisco     IOSv        34140  9BO8LXD6KLX8ZSB7M0PPF
IOSXR-R1    Cisco                 34223
IOS-SW1     Cisco     IOSv        34140  9CGHIOOXX08

hostname    interface                  is_up    is_enabled    description                       speed    mtu  mac_address
----------  -------------------------  -------  ------------  ------------------------------  -------  -----  -----------------
IOS-SW2     GigabitEthernet0/0         True     True                                             1000   1500  50:00:00:03:00:00
IOS-SW2     GigabitEthernet0/1         True     True          Connected to IOSXR-R1 L2-Trunk     1000   1500  50:00:00:03:00:01
IOS-SW2     GigabitEthernet0/2         True     True                                             1000   1500  50:00:00:03:00:02
IOS-SW2     GigabitEthernet0/3         True     True          to_PC_1                            1000   1500  50:00:00:03:00:03
IOS-SW2     GigabitEthernet1/0         True     True                                             1000   1500  50:00:00:03:00:04
IOS-SW2     GigabitEthernet1/1         True     True                                             1000   1500  50:00:00:03:00:05
IOS-SW2     GigabitEthernet1/2         True     True                                             1000   1500  50:00:00:03:00:06
IOS-SW2     GigabitEthernet1/3         True     True                                             1000   1500  50:00:00:03:00:07
IOS-SW2     Vlan10                     True     True          Management 10.10.10.10/24          1000   1500  50:00:00:03:80:0A
IOS-R2      GigabitEthernet0/0         True     True          to_IOS-SW2 Gi0/1                   1000   1500  50:00:00:01:00:00
IOS-R2      GigabitEthernet0/0.30      True     True          to_IOS-SW2 Gi0/1 Vlan 30           1000   1500  50:00:00:01:00:00
IOS-R2      GigabitEthernet0/1         False    False                                            1000   1500  50:00:00:01:00:01
IOS-R2      GigabitEthernet0/2         True     True          to_IOSXR-R1 Gi0/0/0/2 External     1000   1500  50:00:00:01:00:02
IOS-R2      GigabitEthernet0/3         False    False                                            1000   1500  50:00:00:01:00:03
IOS-R2      Loopback0                  True     True                                             8000   1514
IOS-R3      GigabitEthernet0/0         True     True          to_IOSXR-R1 Gi0/0/0/0 External     1000   1500  50:00:00:02:00:00
IOS-R3      GigabitEthernet0/1         False    False                                            1000   1500  50:00:00:02:00:01
....

NAPALM Python Tabla de BGP

Nuevamente vamos a modificar nuestro código para agregar otra funcionalidad más. Esta funcionalidad es para que imprimir en una tabla todos los vecinos BGP de cada routers, incluyendo cuantas rutas están anunciando y cuantos rutas están recibiendo. A continuación el código:

import napalm
from tabulate import tabulate
def main():
    driver_ios = napalm.get_network_driver("ios")
    driver_iosxr = napalm.get_network_driver("iosxr")
    device_list = [["ios-sw2","ios", "switch"],["ios-r2", "ios", "router"],
    ["ios-r3", "ios", "router"],["iosxr-r1", "iosxr", "router"],["ios-sw1", "ios", "switch"]]
    network_devices = []
    for device in device_list:
        if device[1] == "ios":
            network_devices.append(
                            driver_ios(
                            hostname = device[0],
                            username = "codingnetworks",
                            password = "Coding.Networks1"
                            )
                              )
        elif device[1] == "iosxr":
            network_devices.append(
                            driver_iosxr(
                            hostname = device[0],
                            username = "codingnetworks",
                            password = "Coding.Networks1"
                            )
                              )
    devices_table = [["hostname", "vendor", "model", "uptime", "serial_number"]]
    devices_table_int = [["hostname","interface","is_up", "is_enabled", "description", "speed", "mtu", "mac_address"]]
    devices_table_bgp = [["hostname", "neighbor", "remote-as", "status", "sent prefixes", "received prefixes"]]
    for device in network_devices:
        print("Connecting to {} ...".format(device.hostname))
        device.open()
        print("Getting device facts")
        device_facts = device.get_facts()
        devices_table.append([device_facts["hostname"],
                              device_facts["vendor"],
                              device_facts["model"],
                              device_facts["uptime"],
                              device_facts["serial_number"]
                              ])
        print("Getting device interfaces")
        device_interfaces = device.get_interfaces()
        for interface in device_interfaces:
            devices_table_int.append([device_facts["hostname"],
                                  interface,
                                  device_interfaces[interface]['is_up'],
                                  device_interfaces[interface]['is_enabled'],
                                  device_interfaces[interface]['description'],
                                  device_interfaces[interface]['speed'],
                                  device_interfaces[interface]['mtu'],
                                  device_interfaces[interface]['mac_address']
        ])
        if not "SW" in device_facts["hostname"]:
            print("Getting device BGP Neighbors")
            device_bgp_peers = device.get_bgp_neighbors()
            address_fam = "ipv4 unicast"
            if "IOSXR" in device_facts["hostname"]:
                address_fam = "ipv4"
            for bgp_neighbor in device_bgp_peers['global']['peers']:
                devices_table_bgp.append([device_facts["hostname"],
                                          bgp_neighbor,
                                          device_bgp_peers['global']['peers'][bgp_neighbor]['remote_as'],
                                          device_bgp_peers['global']['peers'][bgp_neighbor]['is_up'],
                                          device_bgp_peers['global']['peers'][bgp_neighbor]['address_family'][address_fam]['sent_prefixes'],
                                          device_bgp_peers['global']['peers'][bgp_neighbor]['address_family'][address_fam]['received_prefixes']
                                     ])
        device.close()
        print("Done.")
    print(tabulate(devices_table, headers="firstrow"))
    print()
    print(tabulate(devices_table_int, headers="firstrow"))
    print()
    print(tabulate(devices_table_bgp, headers="firstrow"))
if __name__ == '__main__':
    main()

Salida al correr el código:

Connecting to ios-sw2 ...
Getting device facts
Getting device interfaces
Done.
Connecting to ios-r2 ...
Getting device facts
Getting device interfaces
Done.
Connecting to ios-r3 ...
Getting device facts
Getting device interfaces
Done.
Connecting to iosxr-r1 ...
Getting device facts
'list' object has no attribute 'xpath'
'list' object has no attribute 'xpath'
'list' object has no attribute 'xpath'
Getting device interfaces
Done.
Connecting to ios-sw1 ...
Getting device facts
Getting device interfaces
Done.
hostname    vendor    model      uptime  serial_number
----------  --------  -------  --------  ---------------------
IOS-SW2     Cisco     IOSv        34080  9162JOW336J
IOS-R2      Cisco     IOSv        34140  9MJNBPR604WW2WN1QPABR
IOS-R3      Cisco     IOSv        34140  9BO8LXD6KLX8ZSB7M0PPF
IOSXR-R1    Cisco                 34223
IOS-SW1     Cisco     IOSv        34140  9CGHIOOXX08

hostname    interface                  is_up    is_enabled    description                       speed    mtu  mac_address
----------  -------------------------  -------  ------------  ------------------------------  -------  -----  -----------------
IOS-SW2     GigabitEthernet0/0         True     True                                             1000   1500  50:00:00:03:00:00
IOS-SW2     GigabitEthernet0/1         True     True          Connected to IOSXR-R1 L2-Trunk     1000   1500  50:00:00:03:00:01
IOS-SW2     GigabitEthernet0/2         True     True                                             1000   1500  50:00:00:03:00:02
IOS-SW2     GigabitEthernet0/3         True     True          to_PC_1                            1000   1500  50:00:00:03:00:03
IOS-SW2     GigabitEthernet1/0         True     True                                             1000   1500  50:00:00:03:00:04
IOS-SW2     GigabitEthernet1/1         True     True                                             1000   1500  50:00:00:03:00:05
IOS-SW2     GigabitEthernet1/2         True     True                                             1000   1500  50:00:00:03:00:06
IOS-SW2     GigabitEthernet1/3         True     True                                             1000   1500  50:00:00:03:00:07
IOS-SW2     Vlan10                     True     True          Management 10.10.10.10/24          1000   1500  50:00:00:03:80:0A
IOS-R2      GigabitEthernet0/0         True     True          to_IOS-SW2 Gi0/1                   1000   1500  50:00:00:01:00:00
IOS-R2      GigabitEthernet0/0.30      True     True          to_IOS-SW2 Gi0/1 Vlan 30           1000   1500  50:00:00:01:00:00
IOS-R2      GigabitEthernet0/1         False    False                                            1000   1500  50:00:00:01:00:01
IOS-R2      GigabitEthernet0/2         True     True          to_IOSXR-R1 Gi0/0/0/2 External     1000   1500  50:00:00:01:00:02
IOS-R2      GigabitEthernet0/3         False    False                                            1000   1500  50:00:00:01:00:03
IOS-R2      Loopback0                  True     True                                             8000   1514
IOS-R3      GigabitEthernet0/0         True     True          to_IOSXR-R1 Gi0/0/0/0 External     1000   1500  50:00:00:02:00:00
IOS-R3      GigabitEthernet0/1         False    False                                            1000   1500  50:00:00:02:00:01

Listo ahí tenemos nuestra tabla de vecinos de BGP.

Conclusión

Hemos visto con ejemplos interesantes en routers Cisco las características principales de NAPALM. No tienes que preocuparte de que los dispositivos manejen diferentes sistemas operativos o que sus comandos no son los mismos. La capa de abstracción de NAPALM nos permite reutilizar el mismo código con los diferentes fabricantes. Esto genial, verdad?

Es sorprendente lo poderoso que es NAPALM. Como con tan pocas líneas de código podemos colectar tantos datos y presentarlos de esta manera. Sé que al igual que yo ustedes deben estar imaginando un mundo de aplicaciones y usos que le pueden ayudar en las redes que ustedes son responsables de mantener y operar. Así que no lo piensen mucho, armen sus laboratorios y empiecen a crear.

By Michael Alvarez