NAPALM Multivendor Config
Network Programmability and AutomationTutoriales

NAPALM Network Automation Python: Configurar una Red de Múltiples Fabricantes

Introducción

En este artículo vamos conocer las funciones y métodos de NAPALM para configurar routers y switches de una red de múltiples fabricantes. También haremos uso de dos importantes herramientas utilizadas en la automatización, Jinja y YAML. Este es una continuación del artículo en el que Colectando datos en una red multifabricantes 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 Python

Es en esta sección donde empezaremos a ver y disfrutar de las bondades de utilizar NAPALM para programabilidad y automatización de redes en los dispositivos Cisco y Huawei. Y es donde básicamente les comparto el código de lo visto en el video.

Jinja Template y YAML

Jinja básicamente es un motor utilizado para crear plantillas en Python. Con Jinja pretendemos crear las plantillas de configuración de nuestros routers.

YAML por otro lado es un estándar para serializar datos en una forma legible para los humanos. Con YAML pretendemos definir los parámetros o valores que utilizaremos en las plantillas de Jinja.

Vamos a verlo para que sea más fácil entenderlo.

Cisco_template.yml

interfaces:
    GigabitEthernet0/1:
       description: "to_VRP-R2 Eth1/0/1 External"
       ipv4_addr: 172.17.3.1
       ipv4_mask: 255.255.255.252
bgp:
    as: 300
    neighbors:
        vrp_router:
            as: 200
            ipv4_addr: 172.17.3.2

Huawei_template.yml

interfaces:
    Ethernet1/0/1:
       description: "to_IOS-R3 Gi0/1 External"
       ipv4_addr: 172.17.3.2
       ipv4_mask: 255.255.255.252
bgp:
    as: 200
    neighbors:
        ios_router:
            as: 300
            ipv4_addr: 172.17.3.1

Cisco_template.j2

{% for if_name,if_data in interfaces.items() %}
interface {{if_name}}
 description {{if_data.description}}
 ip address {{if_data.ipv4_addr}} {{if_data.ipv4_mask}}
 no shutdown
{% endfor %}
!
router bgp {{bgp.as}}
 neighbor {{bgp.neighbors.vrp_router.ipv4_addr}} remote-as {{bgp.neighbors.vrp_router.as}}
 !
 address-family ipv4
  neighbor {{bgp.neighbors.vrp_router.ipv4_addr}} activate
 exit-address-family

Huawei_template.j2

{% for if_name,if_data in interfaces.items() %}
interface {{if_name}}
 description {{if_data.description}}
 ip address {{if_data.ipv4_addr}} {{if_data.ipv4_mask}}
 undo shutdown
 undo dcn
{% endfor %}
#
bgp {{bgp.as}}
 peer {{bgp.neighbors.ios_router.ipv4_addr}} as-number {{bgp.neighbors.ios_router.as}}
 #
 ipv4-family unicast
  peer {{bgp.neighbors.ios_router.ipv4_addr}} enable
#

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

NAPALM Python: Config Code 

Lo que haremos ahora es que editaremos el script que escribimos en el video anterior para los routers iOS y IOS-XR y Huawei VRP.

import napalm
from tabulate import tabulate
from jinja2 import Environment, FileSystemLoader
import yaml
def main():
    driver_ios = napalm.get_network_driver("ios")
    driver_iosxr = napalm.get_network_driver("iosxr")
    driver_vrp = napalm.get_network_driver("ce")
    device_list = [["vrp-r2", "vrp", "router"],["ios-r3", "ios", "router"]]
   # device_list = [["ios-sw2","ios", "switch"],["iosxr-r1", "iosxr", "router"],
   # ["ios-r3", "ios", "router"],["vrp-r2", "vrp", "router"],["vrp-sw1", "vrp", "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"
                            )
                              )
        elif device[1] == "vrp":
            network_devices.append(
                            driver_vrp(
                            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"]
                              ])
        #Testing Getters
        device.load_merge_candidate(filename=None, config=get_template_config(device_facts["vendor"]))
        print("\nDiff:")
        print(device.compare_config())
        # You can commit or discard the candidate changes.
        try:
            choice = raw_input("\nWould you like to commit these changes? [yN]: ")
        except NameError:
            choice = input("\nWould you like to commit these changes? [yN]: ")
        if choice == "y":
            print("Committing ...")
            device.commit_config()
        else:
            print("Discarding ...")
            device.discard_config()
        device.close()
        print("Done.")
    print(tabulate(devices_table, headers="firstrow"))
def get_template_config(vendor):
    #This loads data from YAML into Python dictionary
    config_data = yaml.load(open('{}_template.yml'.format(vendor)), Loader=yaml.FullLoader)
    #This line uses the current directory and loads the jinja2 template
    env = Environment(loader = FileSystemLoader('.'), trim_blocks=True, lstrip_blocks=True)
    template = env.get_template('{}_template.j2'.format(vendor))
    #Return the template with data
    print(template.render(config_data))
    return(template.render(config_data))
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 vrp-r2 ...
Getting device facts
interface Ethernet1/0/1
 description to_IOS-R3 Gi0/1 External
 ip address 172.17.3.2 255.255.255.252
 undo shutdown
 undo dcn
#
bgp 200
 peer 172.17.3.1 as-number 300
 #
 ipv4-family unicast
  peer 172.17.3.1 enable
#

Diff:
 description to_IOS-R3 Gi0/1 External
 ip address 172.17.3.2 255.255.255.252
 peer 172.17.3.1 as-number 300
  peer 172.17.3.1 enable

Would you like to commit these changes? [yN]: y
Committing ...
Done.
Connecting to ios-r3 ...
Getting device facts
interface GigabitEthernet0/1
 description to_VRP-R2 Eth1/0/1 External
 ip address 172.17.3.1 255.255.255.252
 no shutdown
!
router bgp 300
 neighbor 172.17.3.2 remote-as 200
 !
 address-family ipv4
  neighbor 172.17.3.2 activate
 exit-address-family

Diff:
+interface GigabitEthernet0/1
+ description to_VRP-R2 Eth1/0/1 External
+ ip address 172.17.3.1 255.255.255.252
- no shutdown
+router bgp 300
+ neighbor 172.17.3.2 remote-as 200
+ address-family ipv4
+  neighbor 172.17.3.2 activate

Would you like to commit these changes? [yN]: y
Committing ...
Done.
hostname    vendor    model      uptime  serial_number
----------  --------  -------  --------  ---------------------
VRP-R2      Huawei    NE40E        1020  []
IOS-R3      Cisco     IOSv         1140  95ZAVRE4Q305F6ET85347

Conclusión

Apreciemos como con la Programabilidad de Redes configuramos interfaces y un peering BGP. Para un solo Peering probablemente resulte más rápido hacer esta configuración por la línea de comandos. 

La verdad es que este ejemplo tiene sentido cuando nos toca realizar configuraciones masivamente. Además de que es más rápido, con el uso de las plantillas Jinja y YAML se reduce el riesgo de errores humanos y se mantiene consistencia en las configuraciones.

Otro punto es que, nosotros pudimos haber hecho la verificación utilizando Programabilidad, haciendo uso del  código  que creamos en videos anteriores para imprimir en una tabla los Peerings BGP de nuestra red. Pero esta verificación se las dejo de tarea.

By Michael Alvarez

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *