Junos PyEZ na przykładzie zadania dotyczącego wyszukiwania wolnych podsieci ipv4

Artykuł o pracy z Junos PyEZ — "mikroframework Python, który umożliwia zarządzanie i automatyzację urządzeń działających na Junos OS" automatyzacja i zarządzanie, to wszystko, co kochamy. Pisanie skryptu opisanego w tym artykule miało kilka celów — nauka Pythona i automatyzacja zadań związanych z gromadzeniem informacji lub zmianą konfiguracji na sprzęcie działającym pod Junos OS. Wybór tej kombinacji Python + Junos PyEZ został dokonany ze względu na niski próg wstępu do języka programowania Python oraz prostotę użycia biblioteki Junos PyEZ, która nie wymaga eksperckiej wiedzy na temat Junos OS.

Zadanie

Audyt wolnych podsieci ipv4 należących do firmy. Kryterium, że podsieć jest wolna — jest brak zapisu o niej w trasach na przełączniku pełniącym rolę routera działającego pod Junos OS.

Realizacja

Python + Junos PyEZ, mimo że był pokusa, by zrobić to przez paramiko i ssh.exec_command, w konsekwencji będzie trzeba na badanym sprzęcie skonfigurować protokół zarządzania urządzeniami netconf. Netconf komunikuje się ze sprzętem poprzez zdalne wywołanie procedur (remote procedure call RPC) i używa XML, w rozważanym przykładzie, do dostarczania uzyskanych informacji.

Instalacja bieżącej wersji Junos PyEZ z PyPI wykonuje się następującą komendą:

$ pip install junos-eznc

Można także zainstalować z głównej gałęzi projektu na GitHubie następującą komendą:

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

Oraz kolejna opcja przez

$ pip install -r requirements.txt 

ta komenda zainstaluje brakujące w systemie biblioteki potrzebne do działania. W mojej wersji requirements.txt jest ich tylko dwie, ostatnie wersje w momencie pisania skryptu:

junos-eznc
netaddr

Skrypt domyślnie pobiera nazwę bieżącego użytkownika w systemie, zalogować się pod inną nazwą użytkownika można używając klucza show_route.py -u , getpass.getpass pobiera hasło z stdin, więc hasło nie pozostanie w systemie. Aby połączyć się z urządzeniem, również trzeba wprowadzić na żądanie jego hostname lub adres IP. Wszystkie dane potrzebne do autoryzacji na urządzeniu zostały uzyskane.

Junos PyEZ wspiera połączenie z urządzeniami działającymi na Junos OS korzystając z konsoli, telnet lub netconf przez ssh. W artykule omówiono ostatnią opcję.

Do połączenia z urządzeniem używa się klasy Device modułu jnpr.junos

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

Wykonuje się zapytanie o wszystkie znane routerowi trasy za pomocą zdalnego wywołania procedur lub wywołania zdalnych procedur, w zależności od tego, co jest wygodniejsze.

data = dev.rpc.get_route_information()

Podobna komenda w systemie Junos OS

user@router> show route | display xml

Dodając na końcu komendy rpc, otrzymujemy znacznik zapytania i możemy go powiązać z nazwą metody RPC, w ten sposób można poznać również inne interesujące nazwy. Warto zauważyć, że składnia pisania znacznika zapytania różni się od nazwy metody, mianowicie należy zastąpić znaki myślnika dolnymi podkreśleniami.

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

Pozostałą część owinąłem w pętli while, aby nie wykonywać ponownie zapytania do routera, jeśli trzeba będzie sprawdzić w innej podsieci, z tych, o których router już wie. Należy wspomnieć, że router, na którym wykonuję zapytanie, zna trasy tylko przez OSPF, dlatego dla routera brzegowego lepiej nieco zmienić zapytanie, aby skrócić czas działania skryptu.

data = dev.rpc.get_ospf_route_information()

Teraz zwrócimy uwagę na zawartość pętli while

Na początku użytkownikowi zostanie zaproponowane wprowadzenie podsieci z maską oraz nie więcej niż trzech oktetów z tej samej podsieci, co jest konieczne do określenia zakresu wyszukiwania. Nie bardzo podoba mi się taka implementacja kryteriów oraz zakresu wyszukiwania, ale na razie nie znalazłem lepszego rozwiązania. Następnie z uzyskanego listy podsieci route_list, korzystając z zmiennej zawierającej nie więcej niż trzy oktety, wybieram interesujące mnie podsieci.

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

Poprzez IPNetwork z modułu netaddr, otrzymuję podsieci w postaci listy adresów ipv4.

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

Używając IPNetwork z wprowadzonej przez użytkownika sieci z maską, otrzymuję zakres adresów i formułuję listę wszystkich adresów z tego zakresu do porównania z listą zajętych adresów.

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

Uzyskany listę wolnych adresów wyświetlam w postaci podsieci.

print(netaddr.IPSet(freeip))

Poniżej znajduje się cały skrypt, testowany na przełącznikach używanych w roli routera, modele 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

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster