Artículo sobre trabajar con Junos PyEZ — “microframework de Python que le permite gestionar y automatizar dispositivos que ejecutan Junos OS” automatización y gestión, todo lo que nos gusta. La escritura del script descrito en este artículo perseguía varios objetivos: estudiar Python y automatizar tareas de recolección de información o cambios de configuración en el hardware que ejecuta Junos OS. La elección de esta combinación de Python + Junos PyEZ se hizo debido a la baja barrera de entrada al lenguaje de programación Python y la facilidad de uso de la biblioteca Junos PyEZ, que no requiere conocimientos expertos de Junos OS.
Tarea
Auditoría de subredes ipv4 libres pertenecientes a la empresa. El criterio de que una subred está libre es la ausencia de una entrada sobre ella en las rutas del conmutador que actúa como router ejecutando Junos OS.
Implementación
Python + Junos PyEZ, aunque hubo la tentación de hacerlo a través de paramiko y ssh.exec_command, como consecuencia, será necesario configurar el protocolo de gestión de dispositivos de red netconf en el equipo consultado. Netconf trabaja con el hardware mediante llamadas de procedimiento remoto (remote procedure call RPC) y utiliza XML, en el ejemplo considerado, para proporcionar la información obtenida.
La instalación de la versión actual de Junos PyEZ desde PyPI se realiza con el siguiente comando:
$ pip install junos-ezncTambién se puede instalar desde la rama principal del proyecto en GitHub con el siguiente comando:
$ pip install git+https://github.com/Juniper/py-junos-eznc.gitY otra opción a través de
$ pip install -r requirements.txt este comando instalará las bibliotecas que faltan en el sistema necesarias para su funcionamiento. En mi versión requirements.txt son solo dos, las versiones se indicaron como las más recientes en el momento de escribir el script:
junos-eznc
netaddrEl script por defecto toma el nombre del usuario actual en el sistema, para iniciar sesión con el nombre de otro usuario se puede usar la clave show_route.py -u getpass.getpass toma la contraseña de stdin para que la contraseña no quede en el sistema. Para conectarse al hardware también se necesitará ingresar bajo solicitud su hostname o dirección ip. Todos los datos necesarios para la autorización en el dispositivo se han obtenido.
Junos PyEZ admite la conexión al hardware que ejecuta Junos OS utilizando consola, telnet o netconf a través de ssh. En el artículo se examina la última opción.
Para conectarse al hardware se utiliza la clase Device del módulo jnpr.junos
with jnpr.junos.Device(host=router,
user=args.name,
passwd=password) as dev:Se realiza una solicitud sobre todas las rutas conocidas por el enrutador a través de una llamada de procedimiento remoto o llamada de procedimientos remotos, como le resulte más conveniente.
data = dev.rpc.get_route_information()Comando similar en Junos OS
user@router> show route | display xmlAgregando 'rpc' al final del comando, obtendremos la etiqueta de solicitud y podemos asociarla con el nombre del método RPC, de esta manera también podemos conocer otros nombres de interés. Cabe señalar que la sintaxis de la etiqueta de solicitud difiere del nombre del método, a saber, hay que reemplazar los guiones por guiones bajos.
user@router> show route | display xml rpc
route_list = data.xpath("//rt-destination/text()")La parte restante la envolví en un bucle while, para no ejecutar la solicitud al enrutador nuevamente, si se necesita verificar en otra subred de las que el enrutador ya conoce. Es importante mencionar que el enrutador del que se consulta conoce rutas solo a través de OSPF, por lo tanto, para un enrutador de borde es mejor modificar un poco la solicitud para reducir el tiempo de ejecución del script.
data = dev.rpc.get_ospf_route_information()Ahora nos dirigimos al contenido del bucle while
Al principio, se le pedirá al usuario que ingrese la subred con una máscara y no más de tres octetos de la red de esa misma subred, esto es necesario para establecer el rango de búsqueda. No me gusta mucho esa implementación del criterio y rango de búsqueda, pero hasta ahora no he encontrado una mejor solución. Luego, del listado de subredes route_list, utilizando la variable que contiene no más de tres octetos, selecciono las subredes que me interesan.
tmp = re.search(r'^%sS*' % subnet_search, route_list[i])A través de IPNetwork, del módulo netaddr, obtengo subredes en forma de lista de direcciones ipv4.
range_subnet = netaddr.IPNetwork(tmp.group(0))Usando IPNetwork de la red introducida por el usuario con máscara, obtengo el rango de direcciones y formo una lista de todas las direcciones de ese rango para compararla con la lista de direcciones ocupadas.
for i in set(net_list).difference(set(busyip)):
freeip.append(i)Muestro la lista obtenida de direcciones libres en forma de subredes.
print(netaddr.IPSet(freeip))A continuación se presenta el script completo, probado en conmutadores utilizados como enrutadores, modelos ex4550, ex4600.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import getpass
import netaddr
import re
import sys
import jnpr.junos
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--user',
action='store',
dest='name',
help='Enter login from tacacs if it differs from the '
'username in the system.')
args = parser.parse_args()
if not args.name:
args.name = getpass.getuser() # Return the “login name” of the user.
router = input("Full routers name: ")
password = getpass.getpass("Password: ")
try:
# Authenticates to a device running Junos, for get information about routs
# into xml format and selects by tag.
route_list = []
with jnpr.junos.Device(host=router,
user=args.name,
passwd=password) as dev:
data = dev.rpc.get_route_information()
route_list = data.xpath("//rt-destination/text()")
except (jnpr.junos.exception.ConnectRefusedError,
jnpr.junos.exception.ConnectUnknownHostError) as err:
print("Equipment name or password wrong.")
sys.exit(1)
while True:
subnet = input("Net with mask: ")
subnet_search = input("Input no more three octet: ")
# Gets a list of busy IP addresses from the received subnets.
busyip = []
for i in range(len(route_list)):
tmp = re.search(r'^%sS*' % subnet_search, route_list[i])
if tmp:
range_subnet = netaddr.IPNetwork(tmp.group(0))
for ip in range_subnet:
busyip.append("%s" % ip)
range_subnet = netaddr.IPNetwork(subnet)
# Gets list ip adresses from subnetworks lists.
net_list = []
for ip in range_subnet:
net_list.append("%s" % ip)
# Сomparing lists.
freeip = []
for i in set(net_list).difference(set(busyip)):
freeip.append(i)
print(netaddr.IPSet(freeip))
request = input("To run request again enter yes or y, "
"press 'enter', complete request: ")
if request in ("yes", "y"):
continue
else:
print('Bye')
break
Fuente: habr.com
