cancel
Showing results for 
Search instead for 
Did you mean: 
cancel

Learning Python 3.0 through Switch CLI

344
Views
10
Helpful
1
Comments
Cisco Employee

If you’re like me and are familiar with switch CLI like the back of your hand, this is a handy method to learn a bit of Python. Being a powerful scripting language, Python is commonly used in many scripting tools and SDKs (software development toolkits) for network automation. It might seem counterintuitive to learn Python through switch CLI since its purpose is to avoid the use of traditional CLI. However, understanding Python directly through the switch CLI helps a user understand how to manipulate various functions and data structures available within a switch, and opens the door to more nuanced scripting.

The example below is specific to NX-OS, but even if your background is in IOS-XE or IOS-XR, you might be able to draw parallels from it.

Cisco Nexus switches have a Python interpreter installed on them. It can be accessed from the switch CLI by typing “python” from privileged EXEC mode. The Python interpreter can be used for programmatic access to the switch to perform various tasks including POAP (power-on auto provisioning) and EEM (embedded event manager). Python calls to the switch return data in the JavaScript Object Notation (JSON) format. This allows us to do a lot with the data since JSON is widely used with many network configuration protocols.

Before we begin, let us talk versions. Cisco NX-OS Software Release 9.3(5) releasing shortly will have two versions of Python available natively. Typing “python” into the switch CLI takes us to the Python 2.0 interpreter which runs Python 2.7.11. The command “python3” invokes a new Python 3.0 interpreter which will be released with NX-OS 9.3(5). This runs Python 3.7.3. 

n9300v# python

Warning: Python 2.7 is End of Support, and future NXOS software will deprecate
python 2.7 support. It is recommended for new scripts to use 'python3' instead.
Type "python3" to use the new shell. 

Python 2.7.11 (default, Jun  4 2020, 09:48:24) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

n9300v# python3
Python 3.7.3 (default, Nov 20 2019, 14:38:01) 
[GCC 5.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

Note: To exit the Python interpreter and return to the NX-OS prompt, use exit() or Ctrl-D.

Let us take a short detour into some of the ways that Python 2 and Python 3 differ. Python 2 is widely deployed in many applications. However, it is now considered a legacy version and Python 3 is the current version used in programming. Most of today’s development efforts involve creating libraries in Python 3, however, many libraries that were created in Python 2 are not forward-compatible.  Python 2 stores strings in the ASCII format whereas Python 3 uses Unicode. And finally, here is an important numerical difference followed by a syntactical one: Python 2 rounds down calculations to the nearest whole number, while Python 3 does not (5/2 = 2 in Python 2 but 5/2 = 2.5 in Python 3). The print statement in Python 2 is replaced by a print function in Python 3. For example, print “hello” in Python 2 would be print (“hello”) in Python 3.

From the above synopsis, it is clear that Python 3 is the way forward. So, let us begin!

We begin by typing “show interface brief” on a switch. We would like to view all the interfaces that are up. This is what the output looks like:

n9300v# show interface brief | i up
mgmt0  --     up     172.25.74.84                   1000    1500    
Eth1/1     --      eth  routed up      none         1000(D) --
Lo1           up         --
Lo5           up         --
Lo10          up         --
n9300v# 

Next, we go into the Python 3 interpreter and do a CLI import.

n9300v# python3
Python 3.7.3 (default, Nov 20 2019, 14:38:01) 
[GCC 5.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> from cli import *

The CLI module contains methods that can allow a user to execute show or config CLI commands from Python. 

For example,

>>> cli("show run interface mgmt0")
'\n!Command: show running-config interface mgmt0\n!Running configuration last done at: Fri Jun 26 10:29:36 2020\n!Time: Tue Jun 30 09:45:19 2020\n\nversion 9.3(5) Bios:version  \n\ninterface mgmt0\n  vrf member management\n  ip address 172.25.74.84/23\n\n'
>>> 
>>> cli('configure terminal ; interface loopback 10 ; no shut')

There is a variant of cli called clid() that returns the output in JSON format. Here is a list of all the functions available in the cli library.

>>> dir(cli)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'cli', 'cli_syntax_error', 'clid', 'clip', 'cmd_exec_error', 'data_type_error', 'getenv', 'json', 'os', 'shlex', 'structured_output_not_supported_error', 'subprocess', 'unexpected_error', 'xmltodict']
>>> 

Getting right into the example of “show interface brief”, let us dissect the script below. 

NOTE: If you are trying out the script below, please go line by line and ensure that you have the indentation correct. Indentation is very important in Python!

>>> from cli import *
>>> import json
>>> intflist=json.loads(clid('show interface brief'))   
>>> for row in intflist['TABLE_interface']['ROW_interface']:   
...    if row.get('state')=='up':
...       print(row.get('interface') + ' -> ' + row.get('state'))   
... 
mgmt0 -> up
Ethernet1/1 -> up
loopback1 -> up
loopback5 -> up
loopback10 -> up
>>> 

We are going to begin by typing in the first three lines of code from the above example into our Python interpreter. 

Python stores data in the form of dictionaries and lists. A dictionary is an unordered pair of “keys” and “values” and is accessed using the key. A list is an ordered set of data which is accessed by its position or index. Both of these structures are dynamic and can be nested.

In the output above, we use “json.loads()” to take in a string and return a JSON object. Intflist now has all of the data from “show interface brief” in JSON. Just like that, with a mere three lines of code, we were able to get our data into a format that Python can manipulate!

If you would like to know the format of intflist, use the command below:

>>> type(intflist)
<class 'dict'>
>>> 

As we can see above, intflist is a dictionary. Now, what keys does it have?

>>> print(intflist.keys())
dict_keys(['TABLE_interface'])
>>> 

Next, let us see what TABLE_interface is.

>>> type('TABLE_interface')
<class 'str'>
>>> 

We are going to stop for a minute, and bring in “pprint” or pretty print, a way to print our data on the screen in a nice, readable format.

>>> from pprint import pprint
>>> table = intflist['TABLE_interface']
>>> for i in table:
...    pprint(i)
... 
'ROW_interface'

Going further with this same method, we can see that ROW_interface actually is a dictionary that has all of our data in it! It includes information about interfaces, port channels, VLAN SVIs and more (all of the output from “show interfaces brief”).

>>> row=table['ROW_interface']  
>>> for i in row:
...    pprint(i)
... 
{'interface': 'mgmt0',
 'ip_addr': '172.25.74.84',
 'mtu': '1500',
 'speed': '1000',
 'state': 'up'}
{'interface': 'Ethernet1/1',
 'portmode': 'routed',
 'ratemode': 'D',
 'speed': '1000',
 'state': 'up',
 'state_rsn_desc': 'none',
 'type': 'eth',
 'vlan': '--'}
<<--- snip --->>
{'interface': 'Vlan1',  'svi_admin_state': 'down',  'svi_rsn_desc': 'VLAN/BD is down'} >>>

Now that we understand how the data is organized, it becomes simple to apply a filter with programmatic logic to select only the interfaces that are up and display them. We proceed to type in the next three lines of code from the above example. We use the key “state” and the value “up” in the dictionary to select our entries of interest and display them.

>>> for row in intflist['TABLE_interface']['ROW_interface']:
...    if row.get('state')=='up':   
...       print(row.get('interface') + ' -> ' + row.get('state'))   
... 
mgmt0 -> up
Ethernet1/1 -> up
loopback1 -> up
loopback5 -> up
loopback10 -> up
>>>

And, voila! Here you have all of the interfaces that are operationally up being displayed. If you want to be nit-picky, you’ll notice that your Vlan interfaces do not show up here. Why? Because with our implementation in 9.3(5), SVIs do not have a key defined to denote their state, but only have one for their administrative state. “State” or operational state is only available for the management interface, ethernet interfaces, subinterfaces, port channels and NVE interfaces. 

It is to be noted that we can also save the few lines of code in this script as a .py file on the bootflash and call it from the switch directly. 

I hope you found this example useful to understand how to use the built-in Python 3 interpreter in NX-OS to find your way around programmatic interfaces in switches!



1 Comment
Collaborator
Collaborator

Thanks for writing this up.

This widget could not be displayed.