FEBRUARY 15, 202

Introduction

This blog post will run through how we can use the PyEZ python library to interact with network devices running Juniper JunOS, in a slightly different way than usual. This is something I learned recently and thought it would be useful to share. Some of you may be confused to see PyEz and screen scraping mentioned in the same sentence, but bear with me..

PyEz Recap

Below is a quick reminder of how we may typically use PyEZ to interact with a network device running Juniper JunOS. Here we create a NETCONF session to the device and execute the show version command on it via an RPC and receive XML output.

from jnpr.junos import Device
from lxml import etree

# Connect to the device using context manager
with Device(host="cr1.cloudnetdev.io", user="x", password="x") as dev:

    # Execute show version rpc
    cr = dev.rpc.get_software_information()
    # Print XML output
    print(etree.tostring(cr, encoding="unicode"))

PyEz also provides us with other Modules and Classes that do a myriad of things. From displaying operational data in pre-defined tables, transferring files to network devices using SCP, and even creating compressing folders or files into TGZ format.

Getting CLI/Shell Access

I recently came across some edge cases in our environment where these provided modules did not successfully achieve what I wanted. We were trying to programmatically compress logs into a TGZ file on some of our core routers and found that the operation took rather long (over 10 minutes). The RPC would eventually fail and thus the desired file would not be created. Fiddling with the RPC timeouts was to no avail either.

PyEZ actually provides us with a way we can create an SSH session to a device and send show commands to it as we usually would when screen scraping with a python library such as Netmiko. The example below shows how we can use the PyEZ *StartShell* class to connect to a network device and run some CLI commands via the .run() method. Notice how we also prefix the commands with cli -c.

from jnpr.junos import Device
from jnpr.junos.utils.start_shell import StartShell

# Connect to the device using context manager
with Device(host="cr1.cloudnetdev.io", user="x", password="x") as dev:

	# Create shell connection
    with StartShell(dev) as ss:
    	# Send CLI command to compress logs into .tgz file
    	ss.run('cli -c "file archive compress source /var/log/* destination /var/tmp/re0.tgz"')
        ver = ss.run('cli -c "show version"')
        print(ver)

The truncated output of this script for brevity:

(False, '\\r               \\rHostname: cr1\\r\\nModel: mx204\\r\\nJunos: 19.1R8\\r\\nJUNOS Base OS boot [19.1R1.8]\\r\\n ...)

The .run() method returns a tuple where the 1st element represents the exit code and the 2nd element is the actual returned command output from the network device.

Getting Plain-text Instead of Structured Data

There may be some situations where we just want clean raw output, that is unstructured and easier to consume for humans compared to XML. A situation where this could be useful is during troubleshooting. This example shows how we can set the format attribute of the XML RPC to get it to return text instead of the usual XML.

from jnpr.junos import Device
from lxml import etree

with Device(host='cr1.cloudnetdev.io', user='x', password='x') as dev:

    # Determine RPC for show bgp summary command
    rpc = dev.display_xml_rpc("show bgp summary")

    if type(rpc) is etree._Element:
        # Set XML attribute to get text format
        rpc.attrib['format']='text'
        # Execute RPC and print output
        result = dev.rpc(rpc)
        print(result.text)

    else:
        print(f"XML RPC for command not available: {rpc}")