Junos PyEZ on the example of the task of finding free ipv4 subnets

An article about working with Junos PyEZ - “Python microframework that enables you to manage and automate devices running Junos OS” automation and management, everything we love. Writing the script described in this article had several goals - learning Python and automating the tasks of collecting information or changing the configuration on equipment running Junos OS. The choice of this particular Python + Junos PyEZ bundle was made due to the low threshold for entering the Python programming language and the ease of use of the Junos PyEZ library, which does not require Junos OS expertise.

Task

Audit of free ipv4 subnets owned by the company. The criterion that a subnet is free is the absence of a record about it in the routes on the switch acting as a router running Junos OS.

implementation

Python + Junos PyEZ, although it was tempting to do it via paramiko and ssh.exec_command, as a result, it will be necessary to configure the network device management protocol netconf on the equipment being polled. Netconf works with the hardware via remote procedure call RPC and uses XML, in this example, to provide the information it receives.

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

$ pip install junos-eznc

You can also install 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 another option through

$ pip install -r requirements.txt 

this command will install the libraries that are missing in the system, necessary for work. In my version requirements.txt there are only two of them, the versions are the latest at the time of writing the script:

junos-eznc
netaddr

The default script takes the name of the current user in the system, you can log in under the name of another user using the show_route.py -u key getpass.getpass takes the password from stdin so the password won't be left on the system. To connect to the equipment, you will also need to enter its hostname or ip-address upon request. All data necessary for authorization on the device has been received.

Junos PyEZ supports connecting to hardware running Junos OS using the console, telnet, or netconf over ssh. The article considers the latter option.

To connect to equipment, use the Device class of the jnpr.junos module

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

A request is made for all routes known to the router via remote procedure call or remote procedure call, whichever is more convenient for you.

data = dev.rpc.get_route_information()

Similar command on Junos OS

user@router> show route | display xml

By adding rpc to the end of the command, we get the request tag and we can match it with the name of the RPC method, in this way we can find out other names of interest. It is worth noting that the syntax for writing a request tag differs from the method name, namely, you should replace hyphens with underscores.

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

I wrapped the rest in a while loop so as not to re-request the router if it needs to be checked on a different subnet from those that the router already knows about. It is worth mentioning that the router on which I am requesting knows routes only through OSPF, so for the border router it is better to change the request a bit to reduce the script execution time

data = dev.rpc.get_ospf_route_information()

Now let's look at the contents 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 the same subnet, this is necessary to set the search range. I don’t really like this implementation of setting the search criterion and range, but so far I haven’t found a better solution. Further, from the received list of route_list subnets, using a variable containing no more than three octets, I select the subnets that interest me

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

Through IPNetwork, the netaddr module, I get subnets in the form of a list of ipv4 addresses

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

Using IPNetwork from the network entered by the user with a mask, I get a range of addresses and form a list of all addresses from this range for comparison with the list of busy addresses.

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

I display the resulting list of free addresses in the form of subnets

print(netaddr.IPSet(freeip))

Below is the full script, tested on switches used as a router, 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

Add a comment