Junos Operational Tables and Views
Table of Contents
- What are Operational Tables and Views?
- Tables are
- Views are
- How to find the list of tables available?
- Bash Alternative - (find)
- Import the modules based on the table name
- How to create a python function
- How to call the function
- Taking it a step further - get the physical port
- Amend the try-except clause
- Fully updated code
- And there we have it
Automate network operations with a standardised API
What are Operational Tables and Views?
We already covered operational automation within the Juniper Operational Automation blog of the series. It covered how to use RPC requests to return XML structured data by default. The equivalent of a show command using the CLI on a Junos device. Data was then available to use in a Python native format. We then went on to output the data with styling into a
Tables are:
A table
is a collection of data for a given XML RPC response, storing each set of data under a unique key name. Each key represents the nodes data and is used to get the data when utilised within a view. Like a database, you have a table name and an id for each entries collection of data.
Views are:
A view
is a representation of the XML RPC response tree items, mapping the XML element names to a Python structured dictionary.
How to find the list of tables available?
The files are stored in the op/ sub-directory of the junos.jnpr module of the PyEZ library. We need to find the location of the junos.jnpr module to determine the absolute path. Using the interactive mode in Python3, we can use os's os.path.dirname()
function to find the name of the directory.
Startup the virtual env
If you don't know how to startup a virtual environment, instructions can be found in my previous blog, Installing Junos PyEZ. I'll also be making a copy of the Github connect_ssh_key.py file, used as a base template for the new script named opertational_tables.py. Place in a new directory called operations. In an attempt to tidy up the folder structure.
cd genja-tutorials/PyEZ/
source bin/activate
mkdir operations
cp connect_ssh_key.py operations/operational_table.py
cd operations
Having started up the virtual environment, created the new directory, and made a copy of the connect_ssh_key.py file. We now need to start up an interactive python3 session.
Start python interactively
python3
This will be used as a way to locate the operational tables name in Junos PyEZ.
import jnpr.junos
import os
path = os.path.dirname(jnpr.junos.__file__)
Output>>… directories absolute path
quit()
Directory path
/home/user/genja-tutorials/Pyez/lib/python3.8/site-packages/jnpr/junos
Change to the directory
Change user
to your user name below
cd /home/user/genja-tutorials/Pyez/lib/python3.8/site-packages/jnpr/junos/
We can then grep
for the Table names of the op subdirectory *.yml files. The table names are the same as class names used for import.
grep "Table:" op/*.yml
Result
Below is just a snippet of the tables. You'll have more in your result.
Bash Alternative - (find)
A quicker way of finding the files is using the Bash command find
. find
is capable of finding directories or files anywhere on your Unix-like OS.
find ~/genja-tutorials/pyez -path *op/*.yml -exec grep "Table:" {}
> ~/genja-tutorials/PyEZ/tutorials/operations/Operational_Tables.txt \;
find ~/genja-tutorials/PyEZ:
tellsfind
to use ~/genja-tutorials/PyEZ as the parent directory (top-level)- path:
doesn't treat the/
as a special character allowing you to enter a partial directory and filename*:
is a special glob character in Linux, used to match any number of characters- exec:
allows you to execute another bash command if the result is true. In this instance, we will use thegrep
command to find the string containing "Table:
" in the filename held in the curly braces "{}
". Theexec
command must end with the terminator\;
or ";
">:
redirects the output and creates a new file within the specified directory
The results show the pathnames relative to the current working directory and the predefined operational tables. All in one command!
Sometimes a bash command can be quicker than using Python3 when working with the native file system.
Import the modules based on the table name
The import statement is a combination of the .yml file name minus the extension and the table name held within the named file, from within the directory jnpr/junos/op/ i.e., from jnpr.junos.op.arp import ArpTable
Open up the new file in the operational folder called operation_table.py to start building the script. We'll start by importing the module for ArpTable placed at the top with the rest of the other imports.
from jnpr.junos.op.arp import ArpTable
When creating an object of the selected table class, it requires a Device instance to be passed as an argument. So, prior to creating the *Table object, you need to initiate the Device instance inserted beneath the command print("Are we.."
arp_table = ArpTable(dev)
Once the object has been created you can use the methods associated with the table to start gathering information. Let's start by populating the ArpTable using the get
method. If we print
the instance of How to call the function
from jnpr.junos import Device
from getpass import getpass
import jnpr.junos.exception
from jnpr.junos.op.arp import ArpTable
ipadd = input('Enter hostname or IP address: ')
key_password = getpass('Enter key password: ')
try:
with Device(host=ipadd, password=key_password) as dev:
print('Are we connected?', dev.connected)
arp_table = ArpTable(dev)
arp_table.get()
print(arp_table)
except jnpr.junos.exception.ConnectAuthError as autherr:
print('Check key password', autherr)
The output displays the number of items equal to the number of IP addresses found in the ARP table. If you don't want the entire ARP table, there is the option of passing an argument (arg) or keyword argument (kwarg). In the case of show arp on the command line, we could specify no-resolve as an argument or hostname=10.0.3.2 as a keyword argument. We know hostname is a keyword argument because if you'd just entered hostname on the cli and tried to hit enter, you would get a prompt asking for a value.
How to create a python function
We will create a python function to break up the script and filter the arp_table.get()
command as mentioned a moment ago based on hostname, which would effectively be an IP address.
Let's call the new function get_arp_info
. The function is placed just below the password variable and before the try-except
statement. Python functions need to be created before being called into use.
def get_arp_info(ip_addr, dev):
""" Get the mac address from the arp table for the given
IP address.
"""
arp_table = ArpTable(dev)
print(arp_table.get(hostname=ip_addr))
print(arp_table.items())
items = len(arp_table.keys())
for ip in range(items):
if arp_table[ip]['ip_address'] == ip_addr:
print("\n\nFound the ip address", arp_table[ip]['ip_address'])
return arp_table[ip]['mac_address']
return
We can remove the three statements we added to the try-except
clause as the function now contains them.
def get_arp_info(ip_addr, dev)
- the function name get_arp_info is expecting two arguments. ip_addr,which would be entered by the user, and dev taken from the Device class instanceprint(arp_table.get(hostname=ip_addr))
- it will populate the arp_table with the entries matching the given IP address and print out how many items found. It should just be one, providing there are no duplicatesfor ip in range(items)
- loops through ar - sanity check of times equal to the total number of items in the arp_tableif arp_table[ip]['ip_address'] == ip_addr
- sanity checks the IP addressreturn arp_table[ip]['mac_address']
- return terminates the function and returns the mac address of the filtered IP address to be used in the next function
How to call the function
Before showing you the complete code, two fundamental pieces are missing. The input of the IP address and a variable named mac_addr within the try-except statement after the print statement.
ip_addr = input
is required to get the IP address from the user, rather than hardcoding it.
ip_addr = input("Enter an IP address to locate: ")
mac_addr = get_arp_info(ip_addr, dev)
ip_addr = input()
- will prompt the user to enter an IP address stored as a variable namedip_addr
mac_addr
- is a variable used to store the returned d statement should e below.
Complete code
The complete code with the ip_addr
input and mac_addr
statement should resemble the code below.
from jnpr.junos import Device
from getpass import getpass
import jnpr.junos.exception
from jnpr.junos.op.arp import ArpTable
ipadd = input('Enter hostname or IP address: ')
key_password = getpass('Enter key password: ')
def get_arp_info(ip_addr, dev):
""" Get the mac address from the arp table for the given
IP address.
"""
arp_table = ArpTable(dev)
print(arp_table.get(hostname=ip_addr))
print(arp_table.items())
items = len(arp_table.keys())
for ip in range(items):
if arp_table[ip]['ip_address'] == ip_addr:
print("Found the ip address", arp_table[ip]['ip_address'])
return arp_table[ip]['mac_address']
return
try:
with Device(host=ipadd, password=key_password) as dev:
print('Are we connected?', dev.connected)
ip_addr = input("Enter an IP address to locate: ")
mac_addr = get_port_arp(ip_addr, dev)
except jnpr.junos.exception.ConnectAuthError as autherr:
print('Check key password', autherr)
OutcomeSave the configuration and run the script. Input the IP address of choice when prompted. It should find if it exists on the network. Bear in mind this is using a standalone device. If the network contains multiple devices, the script may need to be tweaked.
Taking it a step further - get the physical port
To take it a step further, we could use the ethernet switching table to get the physical interface of the IP address. Hence, this was the reason for returning the mac address from the get_arp_info
function and creating a variable. Filtering of the ethernet switching table can be done but requires the mac address.
We'll create another function called get_els_ether_table
. The reason for the ELS (Enhanced Layer Switching) at the beginning is a gentle reminder of the type of Junos code we are using. By now, most devices should be using the ELS style of configuration. Otherwise, you are using an old version of Junos for your device.
First, we need to import the ElsEthernetSwitchingTable
class.
from jnpr.junos.op.elsethernetswitchingtable import ElsEthernetSwitchingTable
Then
Add the following after the get_arp_info
function.
def get_els_ether_table(mac_addr, dev):
""" Get the physical interface name from the ethernet table of the given
IP address.
"""
ether = ElsEthernetSwitchingTable(dev)
ether.get(mac_addr)
print("\n\n\nFound the interface.", ether[mac_addr]['logical_interface'],
end="-" * 70)
print()
Amend the try-except clause
We also need to add a little extra within the try-except clause. We'll be passing the mac_addr
variable to the function as an argument. It's placed just below the mac_addr
variable.
if mac_addr:
get_els_ether_table(mac_addr, dev)
else:
print("Not found")
Fully updated code
You can copy the code below or from my GitHub repository operational_table.py.
from jnpr.junos import Device
from getpass import getpass
import jnpr.junos.exception
from jnpr.junos.op.arp import ArpTable
from jnpr.junos.op.elsethernetswitchingtable import ElsEthernetSwitchingTable
ipadd = input('Enter hostname or IP address: ')
key_password = getpass('Enter key password: ')
def get_arp_info(ip_addr, dev):
""" Get the mac address from the arp table for the given
IP address.
"""
arp_table = ArpTable(dev)
print(arp_table.get(hostname=ip_addr))
print(arp_table.items())
items = len(arp_table.keys())
for ip in range(items):
if arp_table[ip]['ip_address'] == ip_addr:
print("\n\nFound the ip address:", arp_table[ip]['ip_address'])
return arp_table[ip]['mac_address']
return
def get_els_ether_table(mac_addr, dev):
""" Get the physical interface name from the ethernet table of the given
IP address.
"""
ether = ElsEthernetSwitchingTable(dev)
ether.get(mac_addr)
print("\n\nFound the interface.", ether[mac_addr]['logical_interface'],
"\n\n", end="-" * 70)
print()
try:
with Device(host=ipadd, password=key_password) as dev:
print('Are we connected?', dev.connected)
ip_addr = input("Enter an IP address to locate: ")
mac_addr = get_port_arp(ip_addr, dev)
if mac_addr:
get_els_ether_table(mac_addr, dev)
else:
print("Not found")
except jnpr.junos.exception.ConnectAuthError as autherr:
print('Check key password', autherr)
And there we have it
The initial extra effort and time to create the script can reduce the time of future checks. Imagine trying this on a larger scale! It would seem like a tedious task when doing it manually. However, amending the script for this would certainly make life easier.