Articolo sul lavoro con Junos PyEZ — “microframework Python che ti consente di gestire e automatizzare dispositivi in esecuzione con Junos OS” automazione e gestione, tutto come ci piace. La scrittura dello script descritto in questo articolo ha perseguito diversi obiettivi — apprendere Python e automatizzare le attività di raccolta informazioni o modifica della configurazione su hardware gestito da Junos OS. La scelta di utilizzare questa combinazione di Python + Junos PyEZ è stata fatta a causa della bassa barriera di ingresso nel linguaggio di programmazione Python e della semplicità d'uso della biblioteca Junos PyEZ, che non richiede conoscenze esperte di Junos OS.
Compito
Audit delle subnet ipv4 disponibili appartenenti all'azienda. Il criterio per determinare se una subnet è libera è l'assenza di una registrazione di essa nelle rotte sullo switch che funge da router in esecuzione con Junos OS.
Implementazione
Python + Junos PyEZ, anche se c'era la tentazione di farlo tramite paramiko e ssh.exec_command, di conseguenza sarà necessario configurare sul dispositivo interrogato il protocollo di gestione rete netconf. Netconf funziona con l'hardware tramite chiamate a procedure remote (remote procedure call RPC) e utilizza XML, nel caso esaminato, per fornire le informazioni ricevute.
L'installazione dell'attuale versione di Junos PyEZ da PyPI avviene con il seguente comando:
$ pip install junos-ezncÈ possibile installare anche dalla versione principale del progetto su GitHub con il seguente comando:
$ pip install git+https://github.com/Juniper/py-junos-eznc.gitE un'ulteriore opzione tramite
$ pip install -r requirements.txt questo comando installerà le librerie mancanti nel sistema necessarie per il funzionamento. Nella mia versione requirements.txt ce ne sono solo due, le versioni sono quelle più recenti al momento della scrittura dello script:
junos-eznc
netaddrLo script per impostazione predefinita prende il nome dell'utente attuale nel sistema, per accedere con il nome di un altro utente è possibile utilizzare la chiave show_route.py -u getpass.getpass accetta la password da stdin quindi la password non rimarrà nel sistema. Per connettersi all'hardware sarà inoltre necessario inserire su richiesta il suo hostname o indirizzo ip. Tutti i dati necessari per l'autenticazione sul dispositivo sono stati ottenuti.
Junos PyEZ supporta la connessione a hardware in esecuzione con Junos OS utilizzando console, telnet o netconf tramite ssh. In questo articolo viene trattata quest'ultima opzione.
Per connettersi all'hardware si utilizza la classe Device del modulo jnpr.junos
con jnpr.junos.Device(host=router,
user=args.name,
passwd=password) as dev:Viene effettuata una richiesta riguardante tutte le rotte conosciute dal router tramite una chiamata di procedura remota o una procedura remota, a chi fa comodo.
data = dev.rpc.get_route_information()Comando equivalente su Junos OS
user@router> show route | display xmlAggiungendo alla fine del comando rpc, otteniamo un tag di richiesta e possiamo associarlo con il nome del metodo RPC, in questo modo è possibile scoprire anche altri nomi di interesse. Vale la pena notare che la sintassi per scrivere il tag di richiesta è diversa dal nome del metodo, cioè i trattini devono essere sostituiti da underscore.
user@router> show route | display xml rpc
route_list = data.xpath("//rt-destination/text()")Il resto l'ho avvolto in un ciclo while, per non eseguire nuovamente la richiesta al router, se sarà necessario verificare un'altra sottorete conosciuta dal router. È importante menzionare che il router da cui richiedo conosce le rotte solo tramite OSPF, quindi per il router di confine sarebbe meglio modificare leggermente la richiesta per ridurre il tempo di esecuzione dello script.
data = dev.rpc.get_ospf_route_information()Ora ci rivolgiamo al contenuto del ciclo while
All'inizio all'utente verrà chiesto di inserire una sottorete con maschera e non più di tre ottetti da questa stessa sottorete, necessario per impostare l'intervallo di ricerca. Non mi piace molto questa implementazione del criterio e dell'intervallo di ricerca, ma per ora non ho trovato soluzioni migliori. Successivamente, dai sottoreti ricevuti in route_list, utilizzo una variabile contenente non più di tre ottetti per selezionare le sottoreti di interesse.
tmp = re.search(r'^%sS*' % subnet_search, route_list[i])Attraverso IPNetwork, il modulo netaddr, ottengo le sottoreti sotto forma di elenco di indirizzi ipv4
range_subnet = netaddr.IPNetwork(tmp.group(0))Utilizzando IPNetwork dalla rete inserita dall'utente con maschera ottengo l'intervallo di indirizzi e creo un elenco di tutti gli indirizzi di questo intervallo per confrontarlo con l'elenco degli indirizzi occupati.
for i in set(net_list).difference(set(busyip)):
freeip.append(i)L'elenco di indirizzi ottenuti viene visualizzato sotto forma di sottoreti
print(netaddr.IPSet(freeip))Di seguito è riportato lo script completo, testato su switch utilizzati come router, modelli 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
Fonte: habr.com
