Junos PyEZ sur l'exemple de la tâche de trouver des sous-réseaux ipv4 gratuits

Un article sur le travail avec Junos PyEZ - «Microframe Python qui vous permet de gérer et d'automatiser les appareils exécutant Junos OS» d'automatisation et de contrôle, tout ce que nous aimons. L'écriture du script décrit dans cet article poursuivait plusieurs objectifs: apprendre Python et automatiser les tâches de collecte d'informations ou de modification des configurations sur les équipements exécutant Junos OS. Le choix de cet ensemble Python + Junos PyEZ particulier a été fait en raison du seuil bas pour entrer dans le langage de programmation Python et de la facilité d'utilisation de la bibliothèque Junos PyEZ, qui ne nécessite pas l'expertise de Junos OS.

Défi


Audit des sous-réseaux gratuits IPv4 appartenant à l'entreprise. Le critère selon lequel le sous-réseau est libre est le manque d'entrées à ce sujet dans les routes du commutateur qui agit comme un routeur exécutant Junos OS.

Implémentation


Python + Junos PyEZ, bien qu'il y ait eu une tentation de le faire via paramiko et ssh.exec_command, en conséquence, il sera nécessaire de configurer le protocole de gestion de réseau pour les appareils netconf sur l'équipement étudié. Netconf fonctionne avec l'équipement via un appel de procédure distante (RPC) et utilise XML, dans cet exemple, pour fournir les informations reçues.

L'installation de la version actuelle de Junos PyEZ à partir de PyPI s'effectue avec la commande suivante:

$ pip install junos-eznc 

Vous pouvez également installer à partir de la branche principale du projet sur GitHub avec la commande suivante:

 $ pip install git+https://github.com/Juniper/py-junos-eznc.git 

Et une option de plus à travers

 $ pip install -r requirements.txt 

cette commande installera les bibliothèques nécessaires au travail manquant dans le système. Dans ma version de requirements.txt il n'y en a que deux, les dernières versions sont indiquées au moment de l'écriture du script:

 junos-eznc netaddr 

Le script par défaut prend le nom de l'utilisateur actuel dans le système, vous pouvez vous connecter sous le nom d'un autre utilisateur à l'aide de la clé show_route.py -u <nom_utilisateur> getpass.getpass accepte le mot de passe de stdin afin que le mot de passe ne reste pas dans le système. Pour vous connecter à l'équipement, vous devrez également saisir son nom d'hôte ou son adresse IP sur demande. Toutes les données nécessaires à l'autorisation sur l'appareil ont été reçues.

Junos PyEZ prend en charge la connexion à un équipement exécutant Junos OS à l'aide de la console, de telnet ou de netconf via ssh. L'article considère la dernière option.

Pour se connecter à l'équipement, la classe Device du module jnpr.junos est utilisée

 with jnpr.junos.Device(host=router, user=args.name, passwd=password) as dev: 

Une demande est faite sur toutes les routes connues du routeur via un appel de procédure distante ou un appel vers des procédures distantes, vers qui cela est plus pratique.

 data = dev.rpc.get_route_information() 

Une commande similaire sur Junos OS

 user@router> show route | display xml 

En ajoutant la commande rpc à la fin de la commande, nous obtenons la balise de demande et pouvons la faire correspondre avec le nom de la méthode RPC, de cette façon, vous pouvez trouver d'autres noms intéressants. Il convient de noter que la syntaxe pour écrire une balise de demande est différente du nom de la méthode, à savoir remplacer les tirets par des traits de soulignement.

 user@router> show route | display xml rpc <rpc-reply xmlns:junos="http://xml.juniper.net/junos/15.1R1/junos"> <rpc> <get-route-information> </get-route-information> </rpc> </rpc-reply> 

J'ai reçu les données d'itinéraire au format xml, dont je n'ai sélectionné que celles qui m'intéressent par la balise <rt-destination> xxx.xxx.xxx.xxx/yy </rt-destination> et les ai écrites dans une variable sous forme de liste sous forme de chaîne, obtenant ainsi une liste sous-réseaux occupés.

 route_list = data.xpath("//rt-destination/text()") 

J'ai enveloppé le reste dans une boucle while afin de ne pas réexécuter la demande au routeur si j'ai besoin de vérifier un sous-réseau différent de ceux que le routeur connaît déjà. Il est à noter que le routeur sur lequel je demande ne connaît les routes que via OSPF, donc pour le routeur frontière, il vaut mieux changer un peu la demande pour réduire le temps du script

 data = dev.rpc.get_ospf_route_information() 

Voyons maintenant le contenu de la boucle while


Au début, l'utilisateur sera invité à entrer un sous-réseau avec un masque et pas plus de trois octets du réseau du même sous-réseau, cela est nécessaire pour définir la plage de recherche. Je n'aime pas vraiment cette implémentation de la définition des critères et de la plage de recherche, mais jusqu'à présent je n'ai pas trouvé de meilleure solution. Ensuite, dans la liste obtenue des sous-réseaux route_list, en utilisant une variable ne contenant pas plus de trois octets, je sélectionne les sous-réseaux qui m'intéressent

 tmp = re.search(r'^%s\S*' % subnet_search, route_list[i]) 

Grâce à IPNetwork, le module netaddr, j'obtiens des sous-réseaux comme une liste d'adresses ipv4

 range_subnet = netaddr.IPNetwork(tmp.group(0)) 

En utilisant IPNetwork à partir du réseau entré par l'utilisateur avec le masque, j'obtiens une plage d'adresses et forme une liste de toutes les adresses de cette plage pour comparaison avec la liste des adresses occupées.

 for i in set(net_list).difference(set(busyip)): freeip.append(i) 

Je déduis la liste des adresses libres reçues sous forme de sous-réseaux

 print(netaddr.IPSet(freeip)) 

Ci-dessous le script complet, testé sur les commutateurs utilisés comme routeur, modèles 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'^%s\S*' % 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 

Source: https://habr.com/ru/post/fr481648/


All Articles