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-ezncYou 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.gitAnd 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
netaddrBy 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 xmlBy 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
