Blog

BGP summary using PyEZ

October 16, 2017 9:20 am

Automation is all the rage these days and the good news is that when you are working with the Junos OS, you have a plethora of tools at your disposal. This article will provide you with a simple script that uses PyEZ to grab the output of the ‘show bgp summary’ command. After this, we will parse the XML output using jxmlease and then iterate the individual BGP peers to display their characteristics. 

python

Enabling the device for NETCONF.

Using the Junos PyEZ microframework allows  you to manage a device running Junos OS via a NETCONF session over SSH. But before this is possible, we need to  enable the service on the router. We can do this with the following configuration command:

set system services netconf ssh

After enabling the router for NETCONF, we should not forget to allow the service in the firewall filter that is used to protect the routing engine. By default, the service will be available through TCP port 830. When you examine the system connections after enabling the service, you should see the device listening to port 830:

lab@MX480-00-RE0-baseline> show system connections | match 830
tcp4       0      0  *.830                    *.*                      LISTEN

 

Determining the RPC

PyEZ offers a cli method that allows us to issue cli commands and scrape the screen. But that is not what we want. What we want to do is have the device return structured data. To get the device to do this, we issue an RPC (Remote Procedure Call). To issue the proper RPC, we first need to find out how we can translate the ‘show bgp summary’ command to an RPC. This is pretty straightforward. We simply log in to a router and add ‘| display xml rpc’ to the cli command that we are interested in:

 
lab@mx> show bgp summary | display xml rpc
<rpc-reply xmlns:junos="http://xml.juniper.net/junos/16.2R1/junos">
    <rpc>
        <get-bgp-summary-information>
        </get-bgp-summary-information>
    </rpc>
    <cli>
        <banner>{master}</banner>
    </cli>
</rpc-reply>

That’s it. The ‘get-bgp-summary-information’ is the part that is relevant to us. We can easily change this into a format that Python accepts. We replace the hyphens with underscores and add ‘()’ at the end like this:  get_bgp_summary_information().

Now that we know the RPC, let’s start examining the script.

 

The script

 

#!/usr/bin/python3
from jnpr.junos import Device
from lxml import etree
import jxmlease
 
username='said'
password='password123'
 
dev = Device(host='10.0.0.135', user=username, password=password, normalize=True)

dev.open()
 
rpc = dev.rpc.get_bgp_summary_information()
rpc_xml = etree.tostring(rpc, pretty_print=True, encoding='unicode')
dev.close()
 
print(rpc_xml)
 
xmlparser = jxmlease.Parser()
result = jxmlease.parse(rpc_xml)
 
print(result)
 
for neighbor in result['bgp-information']['bgp-peer']:
    print('Checking peer with IP address: ' + neighbor['peer-address'])
    print(neighbor['peer-as'])              
    print(neighbor['peer-state'])           
    print(neighbor['elapsed-time'])         
 
for neighbor in result.find_nodes_with_tag('bgp-peer'):
    print('Checking peer with IP address: ' + neighbor['peer-address'])
    print(neighbor['peer-as'])
    print(neighbor['peer-state'])
    print(neighbor['elapsed-time']) 

Let’s go over each section and see what is going on. The first thing we see is a shebang:

#!/usr/bin/python3

This line will indicates what interpreter Linux should use. A Windows machine will simply ignore this statement. 

After this, we encounter the imports. This example script imports three different modules:

from jnpr.junos import Device
from lxml import etree
import jxmlease

After importing the modules, we set the username and password variable:

username='said'
password='password123'

The username and password were set like this to make for an easy example. I suggest you look into a module called ‘getpass’ or use something else to protect your login credentials.

Next up is specifying the connection:

dev = Device(host='10.0.0.135', user=username, password=password, normalize=True)

After instantiating ‘dev’, we call the open method to open a connection to the device:

dev.open()

After opening a connection to the device, the script issues the RPC. Using etree, we place the result into a string. This string is stored in a variable called ‘rpc_xml’:

rpc = dev.rpc.get_bgp_summary_information()
rpc_xml = etree.tostring(rpc, pretty_print=True, encoding='unicode')

We are done getting the information, so we close the session we have with the device:

dev.close()

We have a look at the string we placed into the ‘rpc_xml’ variable. This is rather useless, but the point here is to examine the output before we use jxmlease to parse it:

print(rpc_xml)

The output would look something like this:

xml

Looks like XML to me! Now, we parse the XML string and place the resulting jxmlease object in a variable called result:

xmlparser = jxmlease.Parser()
result = jxmlease.parse(rpc_xml)

After parsing the XML, we print the result:

print(result)

This would look something like this:

jxmlease

This output is a lot more dictionary-like.

We could iterate this dictionary in the way we would iterate the keys of any Python dictionary. In this example script, we iterate the output at the peer level and access the values that we find interesting. This is done with the following piece of code: 

for neighbor in result['bgp-information']['bgp-peer']:
    print('Checking peer with IP address: ' + neighbor['peer-address'])
    print(neighbor['peer-as'])              
    print(neighbor['peer-state'])           
    print(neighbor['elapsed-time'])         

This code will print the IP, the AS, the peer state and the amount of time that the peer is in that state. When I ran the script, the output of this part of the code was as follows:

Checking peer with IP address: 10.10.255.6
65000
Established
25w5d 9:18:01

Checking peer with IP address: 10.255.255.2
65000
Active
46w4d 16:14:42 

Instead of iterating the keys, we can also use the jxmlease ‘find_nodes_with_tag’ method to iterate over whatever we want. In the example script, we iterate the ‘bgp-peer’ again:

for neighbor in result.find_nodes_with_tag('bgp-peer'):
    print('Checking peer with IP address: ' + neighbor['peer-address'])
    print(neighbor['peer-as'])
    print(neighbor['peer-state'])
    print(neighbor['elapsed-time'])

The main idea was to show you different ways on how to iterate the output. This part of the code generates exactly the same output as the previous part of the code:

Checking peer with IP address: 10.10.255.6
65000
Established
25w5d 9:18:01

Checking peer with IP address: 10.255.255.2
65000
Active
46w4d 16:14:42

We just used a for loop and printed some facts about the BGP sessions, woohoo! Even though that is already pretty neat, it is easy to add some additional logic to the program. Really, all you need is just a little bit of flow control and some additional familiarity with Python. For instance, if you only wanted to print the BGP peers that are not in the ‘Established’ state, you could consider the following:

for neighbor in result.find_nodes_with_tag('bgp-peer'):
    if neighbor['peer-state'] != 'Established':

To understand more about flow control, and to get some extra insight into what is possible with Python, I recommend the following book:

automate_the_boring_stugg

The book does not offer you any networking related examples, but it is amazing at explaining the basics of the Python programming language. I think Al Sweigart has done a fantastic job explaining things, even to those with little or no programming experience (me included).

Another thing worth looking into is the Junos PyEZ Developer Guide. It is a pretty comprehensive guide that will show you how to use PyEZ.

pyez_developer_guide

 

Connecting to a Juniper device and parsing structured data is not that hard. My goal was to explain to you how to map a CLI command to an RPC and to walk you through a simple example script. Hopefully, you will be able to use this article as a reference and start doing some automation of your own.

Many thanks to Ben Dale for proofreading this post!

 

Said van de Klundert

Said van de Klundert

Netherlands based networking enthusiast and Juniper Networks ambassador. Has spent most of his career on the service-providers’ side of things. Known to lab up and write about whatever sparked his interest networking wise. Other than that, he is a father to his son, husband to his wife and he enjoys long dinners with friends.

More Posts - Website - Twitter

Categorised in:

  • Aditya Jambhale says:

    How get same output from multiple devices…

  • To be able to use the script to connect to multiple hosts, it will need some slight modifications. First, you create a function that does something. After this, you will need to execute the function for a certain number of devices. This can be done by for looping through a list of devices.

    In the following example, I put a part the previous script into a function. After that, I create a list of routers that I want to log in to.

    Finally, for every host in the list, I execute the function:

    #!/usr/bin/env python
    #
    from jnpr.junos import Device
    from lxml import etree
    import jxmlease
    import os

    username = ‘said’
    password = ‘password123′

    def bgp_summary_peers_down(host):
    dev = Device(host=host, user=username, password=password, normalize=True)
    dev.open()

    rpc = dev.rpc.get_bgp_summary_information()
    rpc_xml = etree.tostring(rpc, pretty_print=True, encoding=’unicode’)
    dev.close()

    xmlparser = jxmlease.Parser()
    result = jxmlease.parse(rpc_xml)

    print(‘\n’ + 120 * ‘*’ + ‘\n’)
    print(‘Checking the BGP neighbors on host {}.’.format(host))
    print(‘\n’ + 120 * ‘*’ + ‘\n’)
    for neighbor in result[‘bgp-information’][‘bgp-peer’]:
    print(‘Checking peer with IP address: ‘ + neighbor[‘peer-address’])
    print(neighbor[‘peer-as’])
    print(neighbor[‘peer-state’])
    print(neighbor[‘elapsed-time’])

    routers = [
    ‘router1’,
    ‘router2’,
    ]

    for host in routers:
    try:
    bgp_summary_peers_down(host)
    except:
    print(‘Something went wrong connecting to host {}’.format(host))

    When I ran this script, I got the following output:

    *******************************************

    Checking the BGP neighbors on host router1.

    *******************************************

    Checking peer with IP address: 10.41.0.56
    6533
    Established
    40w0d 14:23:01
    Checking peer with IP address: 12.29.36.1
    718
    Established
    3w5d 9:58:39

    *******************************************

    Checking the BGP neighbors on host router2.

    *******************************************

    Checking peer with IP address: 4.71.52.17
    33526
    Established
    3w1d 9:18:57
    Checking peer with IP address: 101.1.2.19
    6513
    Established
    40w0d 14:23:05

    When the list of routers grows in size, you can consider storing it in a separate file and importing it. This way, it will not clutter up your script.

    Sorry the response took so long, I totally missed it. And in addition to that, sorry the reply is formatted this way. I could not get the website to display this in another way here in the comment section.

  • farhad says:

    Hi how can I get the RPC for

    show bgp summary logical system EXTERNAL ?
    something similiar to :
    def show_bgp(): # (6)
    read_and_display(“\nBGP summary information:”,
    lambda dev: dev.rpc.get_bgp_summary_information({“format”: “text”}).text)

    but using a logical system.

    • When you issue the ‘show bgp summary logical-system EXTERNAL | display xml rpc’ command, you can see what options you need to pass.

      In order to see the BGP summary information for logical system EXTERNAL, you need to add the logical_system option to the RPC like this:

      rpc = dev.rpc.get_bgp_summary_information(logical_system=’EXTERNAL’)

  • KxT says:

    I for the life of me can’t get it – but how would you access that third level? AKA – Pull the ‘name’ from the rib table for info but also access the info you mention above?

Leave a Reply

Your email address will not be published. Required fields are marked *