Automation & Programmability
Automation & Programmability
Python for Non-Programmers (Part 5)
10.08.15

WHAT IS OPERATIONAL STATE?

 

Operational state is the result of the network configuration and system behavior.  To the network engineer, this information is generally provided by the Junos CLI "show" commands.  We can, for eample, configure interfaces to participate in OSPF.  We can then obtain the operational state of their neighbor relationship with the "show ospf neighbor" command. 

 

These "show" command make up the backbone of troubleshooting, reporting, and auditing tasks.  Performing these tasks manually is often painfully complex and often requires coordinating various pieces of data from different show commands.  Sometimes you need to login to multiple devices to perform network-wide troubleshooting.

 

There is a better way.

 

A DIFFERENT POINT OF VIEW

 

Imagine that all Junos operational data is stored in a "database".  A database is a collection of "tables".  The Junos CLI command "show ospf neighbor" command, for example, provides the a table of ospf-neighbor information from the operational-state database.  Viewed from the CLI that information is rendered into human readable text, but we know that everything inside Junos is constructed as XML.  The Junos CLI provides us with a "view" of the data, and in some cases we can control if we want that view to be "brief", "detailed" or "extensive".

 

The Junos EZ library uses the same idea and concepts and applies them into the Python shell environment.  In this way, you can retrieve table information and extract the fields from the associated view.  The novel aspect of Junos EZ is you get to decide the content of the view - you can decide the name of the fields, and how the information is presented.  Best of all, you don't need to write any Python code do setup Tables and Views - but we'll get to that in a bit.

 

A QUICK TOUR

 

Let's take a quick tour of the "Ethernet Port Table".  First we need to import the Table we want:

 

>>> from jnpr.junos.op.ethport import *

# display the existing items, we see 'EthPortTable'

>>> dir()
['EthPortTable', 'EthPortView', '__builtins__', '__doc__', '__name__', '__package__', 'connect', 'dev', 'dirname', 'join', 'junos', 'loadyaml', 'pp', 'sshconf_find', 'sys']

 

Next we make an association with a Device variable and that Table definition.  This action does not retrieve any data, just creates the association:

 

>>> eths = EthPortTable(dev)

# examining the table, we can see the name of the Device
# bound, and that the table has 0 items, since we haven't
# retrieved anything yet
 
>>> eths
EthPortTable:jnpr-dc-fw: 0 items

 

Next we retrieve the table records.  We have a few options here.  We could retrieve all of the records or we can limit the scope.  This would be the same as either doing "show interfaces" (all) or "show interfaces ge-0/0/0" (scoped).  There is more to say on the scoping capabilities, and you can find more at the online docs, here.

 

For now, just get all of the table data:

 

>>> eths.get()
EthPortTable:jnpr-dc-fw: 3 items

 

We can examine the keys (record names):

 

>>> eths.keys()
['ge-0/0/0', 'ge-0/0/1', 'ge-0/0/2']

 

And even dump all of the items.  You can do this by pretty printing the eths.items(), but I'll leave that as an excercise for the reader.

 

To select a specific record, you can index the selection by either an index number (starting wtih 0 = first record) or by key-name.

 

>>> e0 = eths['ge-0/0/0']

# if we examine the variable, we see that it is a "View"

>>> e0
EthPortView:ge-0/0/0

 

A "View" allows you to access the fields of the record.  To see a list of field-names, you could do:

 

>>> pprint( e0.keys() )
['oper',
 'rx_packets',
 'macaddr',
 'rx_bytes',
 'admin',
 'mtu',
 'running',
 'link_mode',
 'tx_bytes',
 'tx_packets',
 'present']

 

And you can access the field values by name like so:

 

# what is the MAC address?

>>> e0.macaddr
'00:0c:29:eb:a2:b7'

 

 

EXAMPLE TROUBLESHOOTING

 

Let's say that you want to find any interface that is administratively configured to be "up", but happens to be operational "down".  What might that look like, assuming we've got our eths table loaded.  We could do something like this.  Assuming "ge-0/0/1" was in this condition:

 

>>> for port in eths:
...    if port.admin == 'up' and port.oper == 'down':
...       print "port %s is messed up" % port.name
... 
port ge-0/0/1 is messed up

 

 

Once you get into using Python "idioms", you could simply into something that looks like this:

 

>>> [ port.name for port in eths if port.oper == 'down' and port.admin == 'up']
['ge-0/0/1']

 

 

DEFINING YOUR POINT OF VIEW

 

The "power" of any framework is defined by how open and easy it is for someone other than the developer to extend the functionality.  To this end, I've tried to make the process of defining Tables and Views very easy - no programming is actually required.  You simply define your Tables and Views in a YAML (structured text) file and then import the definitions at runime.  You can see examples of these YAML files in the Junos EZ "op" directory, here.

 

Let's see how these files are imported by doing one manually.  I'll load the "EthPortTable" definition explicitly (this is done automatically by the ethport.py file).  Assuming that the file was local to the directory I started in, this would be:

 

>>> from jnpr.junos.factory import loadyaml

# load the definitions into a Python dictionary >>> mydefs = loadyaml('ethport.yml') # inspect mydefs, this is a dictionary of item name to class. >>> mydefs {'EthPortTable': <class 'jnpr.junos.op.rsmaker.EthPortTable'>, 'EthPortView': <class 'jnpr.junos.op.rsmaker.EthPortView'>} # you can then use the EthPortTable like so: >>> EthPortTable = mydefs['EthPortTable'] >>> eths = EthPortTable(dev) >>> eths EthPortTable:jnpr-dc-fw: 0 items >>> eths.get() EthPortTable:jnpr-dc-fw: 3 items

 

 

WHAT'S NEXT?

 

I'll be writing more documentation on the step-by-step guide to writing Table/View YAML files.  Keep an eye on the github repo docs directory.

 

I'll also start working on short "cookbook" articles and post them on the Juniper "TechWiki".  Stay tuned!

 

I hope this series has helped you get started with Junos automation using Python.  Any questions, please reach out to me, or open requests/issues directly on the Junos EZ github repo, here.

 

Happy not-coding!

 

 

 

 


 

 

 

0 Kudos
03.21.14
Nitin Kumar

Need to update the page. 

below import command wont work now.

from jnpr.junos.op.yaml import loadyaml

 

Now it will be:

from jnpr.junos.factory import loadyaml

03.27.14

@Nitin,

 

Thank you, I just fixed the blog. 

10.08.15
Juniper Employee

The eths.get() is not working.  I tried with both an MX router and EX4500 switch.  Can someone tell me how to get this to work?

Thanks.

 

--------------------------------------------------------------------------------------------------------------------------------------

 

>>> eths = EthPortTable(dev)

 

>>> eths
EthPortTable:10.8.2.152 - Table empty

>>> eths.get()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python27\lib\site-packages\jnpr\junos\factory\optable.py", line 63, in get
    self.xml = getattr(self.RPC, self.GET_RPC)(**rpc_args)
  File "C:\Python27\lib\site-packages\jnpr\junos\rpcmeta.py", line 143, in _exec_rpc
    return self._junos.execute(rpc, **dec_args)
  File "C:\Python27\lib\site-packages\jnpr\junos\decorators.py", line 44, in wrapper
    restore_transform = dev.transform
  File "C:\Python27\lib\site-packages\jnpr\junos\device.py", line 230, in transform
    return self._conn._device_handler.transform_reply
AttributeError: 'NoneType' object has no attribute '_device_handler'

 

10.08.15
Distinguished Expert

Hi V

 

I think you forgot to do dev.open(). I see the same error if I don't do it:

 

>>> from jnpr.junos import Device
>>> from jnpr.junos.op.ethport import EthPortTable
>>> dev = Device(host="10.254.0.34", user="****", passwd="*****")
>>> eths = EthPortTable(dev)
>>> eths
EthPortTable:10.254.0.34 - Table empty
>>> eths.get()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/jnpr/junos/factory/optable.py", line 63, in get
    self.xml = getattr(self.RPC, self.GET_RPC)(**rpc_args)
  File "/usr/local/lib/python2.7/dist-packages/jnpr/junos/rpcmeta.py", line 143, in _exec_rpc
    return self._junos.execute(rpc, **dec_args)
  File "/usr/local/lib/python2.7/dist-packages/jnpr/junos/decorators.py", line 44, in wrapper
    restore_transform = dev.transform
  File "/usr/local/lib/python2.7/dist-packages/jnpr/junos/device.py", line 230, in transform
    return self._conn._device_handler.transform_reply
AttributeError: 'NoneType' object has no attribute '_device_handler'
>>> dev.open()
Device(10.254.0.34)
>>> eths.get()
EthPortTable:10.254.0.34: 4 items
>>> 
10.08.15
Juniper Employee

That resolved the issue, thank you PK !

Top Kudoed Authors