Junos PyEZ uzante la ekzemplon de la tasko serĉi senpagajn ipv4-subretojn

Artikolo pri laborado kun Junos PyEZ - "Python mikrokadro, kiu ebligas al vi administri kaj aŭtomatigi aparatojn kun Junos OS" aŭtomatigo kaj administrado, ĉio, kion ni amas. Skribi la skripton priskribitan en ĉi tiu artikolo havis plurajn celojn - lerni Python kaj aŭtomatigi taskojn por kolekti informojn aŭ ŝanĝi agordojn sur ekipaĵoj kun Junos OS. La elekto de ĉi tiu specifa kombinaĵo de Python + Junos PyEZ estis farita pro la malalta baro al eniro en la programlingvon Python kaj la facileco de uzado de la biblioteko Junos PyEZ, kiu ne postulas spertan scion pri Junos OS.

Objektivo

Revizio de senpagaj ipv4-subretoj apartenantaj al la firmao. La kriterio ke subreto estas senpaga estas la foresto de eniro pri ĝi en la itineroj sur la ŝaltilo funkcianta kiel enkursigilo pri Junos OS.

Реализация

Python + Junos PyEZ, kvankam estis tento fari ĝin per paramiko kaj ssh.exec_command, Kiel rezulto, vi devos agordi la netconf-aparatan retadministran protokolon sur la ekipaĵo estanta enketita. Netconf funkcias kun aparataro per fora proceduro alvoko RPC kaj uzas XML, en ĉi tiu ekzemplo, por disponigi la informojn kiujn ĝi ricevas.

Instali la nunan version de Junos PyEZ de PyPI estas farita per la sekva komando:

$ pip install junos-eznc

Vi ankaŭ povas instali de la ĉefa branĉo de la projekto sur GitHub per la sekva komando:

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

Kaj unu plia opcio per

$ pip install -r requirements.txt 

Ĉi tiu komando instalos bibliotekojn kiuj mankas el la sistemo kaj estas necesaj por funkciado. En mia versio postuloj.txt Estas nur du el ili, la plej novaj versioj estas indikitaj en la momento de verkado de la skripto:

junos-eznc
netaddr

Defaŭlte, la skripto prenas la nomon de la nuna uzanto en la sistemo; vi povas ensaluti sub la nomo de alia uzanto per la show_route.py -u ŝlosilo getpass.getpass prenas la pasvorton de stdin do la pasvorto ne restos en la sistemo. Por konektiĝi al la ekipaĵo, vi ankaŭ devos enigi ĝian gastigan nomon aŭ IP-adreson kiam oni petas. Ĉiuj datumoj necesaj por rajtigo sur la aparato estis ricevitaj.

Junos PyEZ subtenas konekton al ekipaĵo funkcianta Junos OS uzante la konzolon, telnet aŭ netconf per ssh. La artikolo diskutas la lastan opcion.

Por konektiĝi al ekipaĵo, uzu la Aparato-klason de la modulo jnpr.junos

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

Peto estas farita por ĉiuj itineroj konataj al la enkursigilo per malproksima procedalvoko aŭ fora procedalvoko, kiu ajn estas pli oportuna.

data = dev.rpc.get_route_information()

Simila komando ĉe Junos OS

user@router> show route | display xml

Aldonante rpc al la fino de la komando, ni ricevas petan etikedon kaj povas kongrui ĝin kun la nomo de la RPC-metodo, tiamaniere ni povas ekscii aliajn interesajn nomojn. Indas rimarki, ke la sintakso por skribi la petan etikedon diferencas de la metodonomo, nome, vi devus anstataŭigi la streketojn per substrekoj.

user@router> show route | display xml rpc
<rpc-reply >route_list = data.xpath("//rt-destination/text()")

La resto de la parto estis envolvita en iom da buklo, por ne ripeti la peton al la enkursigilo se necesus kontroli alian subreton el tiuj, pri kiuj la enkursigilo jam konas. Menciindas, ke la enkursigilo, sur kiu mi faras la peton, konas itinerojn nur per OSPF, do por randa enkursigilo estas pli bone ŝanĝi la peton iomete por redukti la rultempon de la skripto.

data = dev.rpc.get_ospf_route_information()

Nun ni rigardu la enhavon de la buklo while

Komence, la uzanto estos petita eniri subreton kun masko kaj ne pli ol tri oktetoj de la reto de la sama subreto, tio estas necesa por agordi la serĉan intervalon. Mi ne tre ŝatas ĉi tiun efektivigon de specifado de la kriterioj kaj serĉa gamo, sed ĝis nun mi ne trovis pli bonan solvon. Poste, el la rezulta listo de subretoj route_list, uzante variablon enhavanta ne pli ol tri oktetojn, mi elektas la subretojn kiuj interesas min

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

Per IPNetwork, la modulo netaddr, mi ricevas subretojn en formo de listo de ipv4-adresoj

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

Uzante IPNetwork, mi akiras gamon da adresoj de uzanto-enirita reto kun masko kaj generas liston de ĉiuj adresoj de ĉi tiu gamo por komparo kun la listo de okupataj adresoj.

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

Mi montras la rezultan liston de liberaj adresoj en formo de subretoj

print(netaddr.IPSet(freeip))

Malsupre estas la kompleta skripto, provita sur ŝaltiloj uzataj kiel enkursigilo, modeloj 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

fonto: www.habr.com

Aldoni komenton