Junos PyEZ на прыкладзе задачы па пошуку вольных падсетак ipv4

Артыкул аб рабоце з Junos PyEZ – “Python microframework, які забяспечвае вашу менеджмент і аўтаматычныя прылады для кіравання Junos OS” аўтаматызацыя і кіраванне, усё як мы любім. Напісанне скрыпту апісанага ў гэтым артыкуле пераследвала некалькі мэт - вывучэнне Python і аўтаматызацыя задач па зборы інфармацыі або змены канфігурацыі на абсталяванні пад кіраваннем Junos OS. Выбар іменнай гэтай звязкі Python + Junos PyEZ быў зроблены з-за нізкага парога ўваходжання ў мову праграмавання Python і прастаты выкарыстання бібліятэкі Junos PyEZ, якая не патрабуе экспертных ведаў Junos OS.

Задача

Аўдыт вольных падсетак ipv4 прыналежных кампаніі. Крытэрыем таго, што падсетка вольная - з'яўляецца адсутнасць запісу пра яе ў маршрутах на камутатары які выконвае ролю маршрутызатара пад кіраваннем Junos OS.

Рэалізацыя

Python + Junos PyEZ, хоць была спакуса зрабіць праз paramiko і ssh.exec_command, як следства спатрэбіцца на апытаным абсталяванні наладзіць пратакол сеткавага кіравання прыладамі netconf. Netconf працуе з абсталяваннем пасродкам выдаленага выкліку працэдур (remote procedure call RPC) і выкарыстае XML, у разгляданым прыкладзе, для падавання атрыманай інфармацыі.

Усталяванне бягучай версіі Junos PyEZ з PyPI, выконваецца наступнай камандай:

$ pip install junos-eznc

Можна ўсталяваць таксама з асноўнай галіны праекту на GitHub наступнай камандай:

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

І яшчэ адзін варыянт праз

$ pip install -r requirements.txt 

гэтая каманда ўсталюе адсутныя ў сістэме бібліятэкі неабходныя для працы. У маёй версіі патрабаванні.txt іх усяго дзве, версіі пазначаны апошнія на момант напісання скрыпту:

junos-eznc
netaddr

Скрыпт па змаўчанні бярэ імя бягучага карыстальніка ў сістэме, залагініцца пад імем іншага карыстальніка можна выкарыстоўваючы ключ show_route.py -u getpass.getpass прымае пароль з stdin так пароль не застанецца ў сістэме. Для падлучэння да абсталявання таксама спатрэбіцца ўвесці па запыце яго hostname ці ip-адрас. Усе неабходныя для аўтарызацыі на прыладзе дадзеныя атрыманы.

Junos PyEZ падтрымлівае падлучэнне да абсталявання пад кіраваннем Junos OS выкарыстоўваючы кансоль, telnet ці netconf праз ssh. У артыкуле разгледжаны апошні варыянт.

Для падлучэння да абсталявання выкарыстоўваецца клас Device модуля jnpr.junos

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

Выконваецца запыт аб усіх вядомых роўтэр маршрутах праз выдалены выклік працэдур або выклік выдаленых працэдур, каму як зручней.

data = dev.rpc.get_route_information()

Аналагічная каманда на Junos OS

user@router> show route | display xml

Дадаўшы ў канец каманды rpc, атрымаем тэг запыту і можам супаставіць яго з імем метаду RPC, такім спосабам можна пазнаць і іншыя якія цікавяць імёны. Варта адзначыць, што сінтаксіс напісання тэга запыту адрозніваецца ад імя метаду, а менавіта варта замяніць знакі злучка на ніжнія падкрэсленне.

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

Астатнюю частку абгарнуў у цыкл while, каб не выконваць паўторна запыт на роўтар, калі трэба будзе праверыць у іншай падсетцы з тых, пра якія роўтэр ужо ведае. Варта згадаць, што роўтар на якім запытваю ведае маршруты толькі праз OSPF, таму для памежнага роўтара лепш змяніць крыху запыт, каб скараціць час працы скрыпту.

data = dev.rpc.get_ospf_route_information()

Цяпер звернемся да змесціва цыклу while

У пачатку карыстачу будзе прапанавана ўвесці падсетку з маскай і не больш за тры актэты з сеткі гэтай жа падсеткі, гэта неабходна для задання дыяпазону пошуку. Не вельмі падабаецца такая рэалізацыя задання крытэрыю і дыяпазону пошуку, але пакуль лепш рашэння не знайшоў. Далей з атрыманага спісу падсетак route_list выкарыстоўваючы зменную якая змяшчае не больш за тры актэты выбіраю цікавыя для мяне падсеткі

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

Праз IPNetwork, модуля netaddr, атрымліваю падсеткі ў выглядзе спісу ipv4 адрасоў

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

Выкарыстоўваючы IPNetwork з уведзенай карыстачом сеткі з маскай атрымліваю дыяпазон адрасоў і фармую спіс усіх адрасоў з гэтага дыяпазону для параўнання са спісам занятых адрасоў.

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

Атрыманы спіс свабодных адрасоў выводжу ў выглядзе падсетак

print(netaddr.IPSet(freeip))

Ніжэй прыведзены скрыпт цалкам, тэсціравалі на камутатарах выкарыстоўваных у ролі маршрутызатара, мадэлі 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

Крыніца: habr.com

Дадаць каментар