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.