Junos PyEZ: An Example of Finding Free IPv4 Subnets

An article about working with Junos PyEZ — a Python microframework that enables you to manage and automate devices running Junos OS — focusing on automation and management, everything we love. The script described in this article aimed at several goals: learning Python and automating tasks related to gathering information or changing configurations on devices running Junos OS. The choice of the Python + Junos PyEZ combination was made due to the low entry threshold of the Python programming language and the ease of use of the Junos PyEZ library, which does not require expert knowledge of Junos OS.

Task

Auditing free IPv4 subnets belonging to the company. The criterion for a subnet being free is the absence of a route entry for it on the switch acting as the router running Junos OS.

Implementation

Python + Junos PyEZ, although there was a temptation to use paramiko and ssh.exec_command, as a consequence, it will be necessary to configure the network management protocol netconf on the accessed equipment. Netconf operates with devices via remote procedure calls (RPC) and uses XML in the discussed example to provide the obtained information.

Installing the current version of Junos PyEZ from PyPI is done with the following command:

$ pip install junos-eznc

You can also install it from the main branch of the project on GitHub with the following command:

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

And one more option via

$ pip install -r requirements.txt 

this command will install the missing libraries required for operation. In my version, on all nodes. there are only two, with their latest versions at the time of writing the script:

junos-eznc
netaddr

By default, the script takes the current user's name in the system; you can log in as another user using the key show_route.py -u . getpass.getpass accepts the password from stdin, so the password will not remain in the system. To connect to the equipment, you will also need to enter its hostname or IP address upon request. All necessary authorization data for the device has been obtained.

Junos PyEZ supports connecting to equipment running Junos OS via console, telnet, or netconf over ssh. This article considers the latter option.

The Device class from the jnpr.junos module is used to connect to the equipment.

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

A request is made for all known routes to the router via remote procedure call, or however it is easier for you.

data = dev.rpc.get_route_information()

The same command on Junos OS

user@router> show route | display xml

By adding 'rpc' at the end of the command, we get the request tag, and we can match it with the RPC method name; this way, we can find out other interesting names as well. It is worth noting that the syntax for writing the request tag differs from the method name; specifically, you need to replace dashes with underscores.

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

I wrapped the remaining part in a while loop, so as not to execute the request on the router again when needing to check in another subnet that the router already knows. It is worth mentioning that the router I am querying knows routes only via OSPF, so for a border router, it's better to slightly modify the request to shorten the script's execution time.

data = dev.rpc.get_ospf_route_information()

Now let's look at the content of the while loop

At the beginning, the user will be prompted to enter a subnet with a mask and no more than three octets from the network of that same subnet; this is necessary for setting the search range. I don't particularly like this implementation for setting the criterion and search range, but I haven't found a better solution yet. Next, from the obtained list of subnets route_list, using a variable containing no more than three octets, I select the subnets of interest.

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

Through IPNetwork from the netaddr module, I obtain subnets in the form of a list of ipv4 addresses.

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

Using IPNetwork from the subnet provided by the user with a mask, I get the address range and form a list of all addresses in that range for comparison with the list of occupied addresses.

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

The obtained list of free addresses is displayed as subnets.

print(netaddr.IPSet(freeip))

Below is the complete script, tested on switches used as routers, models 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

Source: habr.com

Buy reliable website hosting with DDoS protection, VPS VDS servers 🔥 Buy reliable website hosting with DDoS protection, VPS VDS servers | ProHoster