Extending the Junos PyEZ Device class with my own subclass helped me to more easily reuse my code and it made my scripts less of a clutter. This article is a short example on how to do this.

Extending the base Device class:

When you are using PyEZ, you import the Device class and instantiate an object to work with. The following is a pretty straightforward example:

from jnpr.junos import Device

with Device(host='10.0.0.1', user='lab', password='lab123', normalize=True) as router_1:                                          
    rpc = router_1.rpc.get_bgp_summary_information()

In the previous example, the Device class is used to create an object named ‘router_1’. Using the rpc method, the BGP summary information is retrieved from the device. Because we open the connection to the device using a context manager (with), the connection to the device is automatically opened and closed.

As you expand your scripting efforts, using functions will start to make sense. This will make it easier to re-use code. You could put your functions in a single file and import them in other scripts whenever you require them.

Another thing you could consider is to extend the Device class with your own subclass. Have a look at the following example juniper_class.py:

from jnpr.junos import Device

class JunosDevice(Device):

    def get_bgp_summary(self):
        pass     

After the import of Device, the JunosDevice class is defined. The parent class, Device, is passed to it as a parameter.

Because JunosDevice is a child class to Device, all the methods that are available to Device are available to JunosDevice also. You do not have to declare anything to gain access to rpc, cli, facts, etc.

The child class is extended with a method called get_bgp_summary. This is now something that you can do in addition to what Device has to offer. Instead of putting in a pass, let’s make the get_bgp_summary function do something:

from jnpr.junos import Device

class JunosDevice(Device):

    def get_bgp_summary(self):
        """        
        Gathers information from the <get-bgp-summary-information> RPC.
        
        Returns a dictionary:
            { 
                peer-address : {
                    'peer-as' : peer_as,
                    'peer-description' : peer_description,
                    'peer-up-time' : peer_up_time,
                }
            }        
        """
        ret = {}
       
        rpc = self.rpc.get_bgp_summary_information()
        bgp_peers = rpc.findall('.//bgp-peer')
        
        for peer in bgp_peers:
            peer_address = peer.find('./peer-address').text
            peer_as = peer.find('./peer-as').text            
            peer_up_time = peer.find('./elapsed-time').attrib['seconds']
            
            peer_description = None
            if peer.find('./description') is not None:
                peer_description = peer.find('./description').text
            
            ret[peer_address] = {                 
                'peer-as' : peer_as,
                'peer-description' : peer_description,
                'peer-up-time' : peer_up_time,
            }

        return ret     

The subclass now has an extra method that can ‘dictify’ the BGP summary information that we think is interesting with the key / values that we like to work with. Obviously, working with the JunosDevice class is no different than working with the Device class. The following test_juniper_class.py is an example on how you could use it:

from juniper_class import JunosDevice
from pprint import pprint
    
with JunosDevice(host='10.0.0.1', user='lab', password='lab123', normalize=True) as router_1: 
    pprint(router_1.get_bgp_summary())

We import the JunosDevice class and use it to setup a connection with router_1. After this, we call the newly created get_bgp_summary() method which will give us the following:

sh-4.4# python3 test_juniper_class.py
{'10.0.3.48': {'peer-as': '65500',
                'peer-description': 'gr01.dal',
                'peer-up-time': '2524741'},
 '10.0.3.49': {'peer-as': '65500',
                'peer-description': 'gr02.dal',
                'peer-up-time': '2523245'},
 '2001:db8:2:5::8': {'peer-as': '65500',
                      'peer-description': 'ar02.dal',
                      'peer-up-time': '60149802'}}

Closing thoughts:

By making your own class inherit the Juniper PyEZ Device class, it will be equipped with everything that PyEZ comes with. And by extending it with methods that make sense for your environment, you will have something that is very easy to reuse, making future scripts and programs easier to write and maintain.