Artikel over het werken met Junos PyEZ — "Python microframework waarmee je apparaten die draaien op Junos OS kunt beheren en automatiseren" automatisering en beheer, precies zoals we houden. Het schrijven van het script dat in dit artikel wordt beschreven, had verschillende doelstellingen — het leren van Python en het automatiseren van taken voor het verzamelen van informatie of het wijzigen van de configuratie op apparatuur onder Junos OS. De keuze voor deze combinatie van Python + Junos PyEZ is gemaakt vanwege de lage instapdrempel voor de programmeertaal Python en de eenvoud van het gebruik van de Junos PyEZ-bibliotheek, die geen expertkennis van Junos OS vereist.
Task
Audit van vrije ipv4-subnetten die eigendom zijn van het bedrijf. De criterium dat aangeeft dat een subnet vrij is, is het ontbreken van een vermelding in de routes op de switch die fungeert als router onder Junos OS.
Implementatie
Python + Junos PyEZ, hoewel er de verleiding was om het via paramiko en ssh.exec_command te doen, als gevolg daarvan is het nodig om op de geïnterviewde apparatuur het netconf-device management protocol in te stellen. Netconf werkt met apparatuur via remote procedure call (RPC) en gebruikt XML, in het besproken voorbeeld, om de verkregen informatie te verstrekken.
De installatie van de huidige versie van Junos PyEZ uit PyPI gebeurt met de volgende opdracht:
$ pip install junos-ezncJe kunt ook installeren vanaf de hoofdvertakking van het project op GitHub met de volgende opdracht:
$ pip install git+https://github.com/Juniper/py-junos-eznc.gitEn een andere optie via
$ pip install -r requirements.txt deze opdracht installeert de ontbrekende bibliotheken die nodig zijn voor de werking. In mijn versie requirements.txt zijn het er in totaal twee, de versies zijn de laatste op het moment van schrijven van het script:
junos-eznc
netaddrHet script neemt standaard de naam van de huidige gebruiker in het systeem, inloggen onder de naam van een andere gebruiker kan met de sleutel show_route.py -u getpass.getpass accepteert het wachtwoord van stdin zodat het wachtwoord niet in het systeem achterblijft. Voor verbinding met de apparatuur moet ook om de hostname of IP-adres worden gevraagd. Alle noodzakelijke gegevens voor autorisatie op het apparaat zijn verkregen.
Junos PyEZ ondersteunt verbinding met apparatuur die draait op Junos OS via console, telnet of netconf via ssh. In dit artikel wordt de laatste optie besproken.
Voor verbinding met de apparatuur wordt de Device-klasse van de jnpr.junos-module gebruikt
with jnpr.junos.Device(host=router,
user=args.name,
passwd=password) as dev:Een verzoek om alle bekende routes naar de router wordt uitgevoerd via een remote procedure call of een call van remote procedures, afhankelijk van wat handiger is.
data = dev.rpc.get_route_information()Een soortgelijke opdracht in Junos OS
user@router> show route | display xmlDoor 'rpc' aan het einde van de opdracht toe te voegen, krijgen we een request-tag en kunnen we deze vergelijken met de naam van de RPC-methode, op deze manier kunnen we ook andere interessante namen ontdekken. Het is belangrijk op te merken dat de syntaxis van de request-tag verschilt van de naam van de methode; specifiek moeten de koppeltekens worden vervangen door onderstrepingen.
user@router> show route | display xml rpc
route_list = data.xpath("//rt-destination/text()")De rest heb ik in een while-lus verpakt, zodat ik de router niet opnieuw hoef te vragen als ik moet controleren in een ander subnet dat de router al kent. Het is vermeldenswaard dat de router waarop ik de aanvraag doe alleen routes kent via OSPF, dus voor de randrouter is het beter om de aanvraag iets te wijzigen om de uitvoeringstijd van het script te verkorten.
data = dev.rpc.get_ospf_route_information()Laten we nu naar de inhoud van de while-lus kijken.
Aan het begin wordt de gebruiker gevraagd om een subnet met een masker in te voeren en niet meer dan drie oktetten uit dat subnet; dit is nodig om het zoekbereik op te geven. Ik ben niet erg tevreden met deze implementatie van het vaststellen van de criteria en het zoekbereik, maar tot nu toe heb ik geen betere oplossing gevonden. Vervolgens kies ik uit de verkregen lijst van subnets 'route_list' de interessante subnets met behulp van de variabele die niet meer dan drie oktetten bevat.
tmp = re.search(r'^%sS*' % subnet_search, route_list[i])Via IPNetwork, uit de netaddr-module, krijg ik de subnets als een lijst van ipv4-adressen.
range_subnet = netaddr.IPNetwork(tmp.group(0))Door IPNetwork van het door de gebruiker ingevoerde netwerk met masker te gebruiken, krijg ik het bereik van adressen en vorm ik een lijst van alle adressen in dit bereik voor vergelijking met de lijst van bezette adressen.
for i in set(net_list).difference(set(busyip)):
freeip.append(i)De verkregen lijst van vrije adressen geef ik weer als subnets.
print(netaddr.IPSet(freeip))Hieronder staat het volledige script, getest op switches die als router worden gebruikt, modellen 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
Bron: habr.com
