Upload
others
View
46
Download
0
Embed Size (px)
Citation preview
arista-python-web2py DocumentationRelease 001
Anees Mohamed
Sep 13 2017
Contents
1 Preface 3
2 Chapter 1 Introducing Python Core Concepts 521 Python Implementation on Various Operating Systems 622 Python Package Manager (pip) 923 Installing Python Modules 1124 Python Core Concepts 1225 Summary 35
3 Chapter 2 Script Framework for Use Cases 3731 First Script - Show version 3732 Second Script - Running Show Version on Multiple Switches 4033 Third Script - Using External Input 4334 Fourth Script ndash Storing Result in a Dictionary 4935 Fifth Script ndash Handling Exceptions 5136 Summary ndash Script Framework 56
4 Chapter 3 Troubleshooting Use Cases 5941 Monitor Data Plane Drops 5942 Monitor Control Plane Drops 77
5 Chapter 4 Capacity Planning Use Cases 8551 Port Capacity 8652 Hardware Scalability Assessment 98
6 Chapter 5 Web2Py Introduction 12561 Understanding the functionality of the Tool 12662 Web2Py Framework for the Tool 132
7 Chapter 6 Web2Py Installation 13771 Install Required Packages 13772 Install Web2Py 13973 Start Web2Py Service 140
8 Chapter 7 Creating a New Web2Py Application 14381 Default Admin Page 14382 Create a New Application 144
i
83 Edit the Application 147
9 Chapter 8 Web2Py - Prepare the Tool 15391 Add Switches 15492 View Switches 16493 Categorize Switches 16594 Summary 171
10 Chapter 9 Web2Py - Troubleshooting Use Cases 173101 Data Plane Packet Drops 174102 Control Plane Drops 190103 Last 10 Sev 1-3 Log Messages 192
11 Chapter 10 Web2Py - Capacity Planning Use Cases 199111 Port Capacity 200112 Hardware Scale 204113 Summary 206
ii
arista-python-web2py Documentation Release 001
Contents
Contents 1
arista-python-web2py Documentation Release 001
2 Contents
CHAPTER 1
Preface
This book is for network engineers managing Arista network products and have no prior knowledge on any pro-gramming language Python is an easy to learn programming language and more importantly it is a very powerfulprogramming language Python can be used to build programs ranging from automating simple tasks to building mostsophisticated and advanced software applications
So How does anyone can learn Python Typical way of learning programming language is understanding the coreconcepts with general examples Then develop programs for the use cases specific to you using the core concepts Sothe learning cycle is high because you first learn Python with the examples that you donrsquot need and then translate thatlearning to create programs for your use case This is where many of the network engineers lose their learning track
This book addresses this problem by teaching core Python using the example the network engineers need The goalof this book is to help network engineers to automate the common operational and troubleshooting steps Also under-standing the programmability of network devices changes your approach to solve a problem in a creative way
Chapters 1 to 4 discusses Python core concepts Json RPC and Pyeapi modules with the common networking use casessuch as inventory troubleshooting and capacity planning You will be introduced with a framework which you canreuse it for your own network use cases
Chapters 5 to 7 introduces a Python web framework called Web2Py Chapter 8 to 11 enables you to host your Pythonprograms in the Web2Py application If you follow Chapters 1 to 11 in this book you will build and host this webbased tool which can be leveraged by all the network engineers in your organization
3
arista-python-web2py Documentation Release 001
4 Chapter 1 Preface
CHAPTER 2
Chapter 1 Introducing Python Core Concepts
bull Python Implementation on Various Operating Systems
ndash Microsoft Windows
ndash Apple Mac OS X
ndash Ubuntu
bull Python Package Manager (pip)
bull Installing Python Modules
ndash Pyeapi
ndash jsonrpc
bull Python Core Concepts
ndash Data Type
ndash Data Structure
List
Set
Dictionary
ndash Control Flow Statements
if
for
ndash Data Operations
String
Integer
5
arista-python-web2py Documentation Release 001
ndash Simple Use Cases
ndash Script File
ndash Modules
pprint
sys
re - Regular Expression
ndash Handling Structured Data
Text
JSON
YAML
bull Summary
Network engineers require to connect to network devices collect data using network OS commands and parse throughthe data to discover the required information With that in mind Pythonrsquos core concepts such as data types operationsdata structures control flow statements and modules are discussed in this chapter Before discussing the core conceptsPython implementation on various operating systems need to be discussed
Python Implementation on Various Operating Systems
Python implementation on Microsoft Windows Apple Macrsquos OS X Ubuntu Linux distribution and Arista ExtensibleOperation System (EOS) is discussed in this section There are two tracks of Python versions available today 2x and3x Python version 2x is widely deployed and the industry will eventually move to 3x Python programs in this bookare written using the version 27x
Microsoft Windows
Python is not installed by default on Microsoft Windows operating systems You can download and install pythonfrom wwwpythonorg website For this book It is recommended to download and install Python version 27x Bydefault Python will be installed in the cPython27 folder After installing Python PATH variable needs to be updatedwith the path to the Python installation folder
6 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
After changing the PATH variable launch the Windows command prompt and verify you can invoke the Pythoninterpreter
Python interpreter is located in the folder CPython27 Python standard library modules are located inCPython27folder
21 Python Implementation on Various Operating Systems 7
arista-python-web2py Documentation Release 001
Apple Mac OS X
Apple Mac OS X comes with Python 27x You can verify by invoking the interpreter from the terminal If yourOS X version does not have Python installed by default you can download and install python from wwwpythonorgwebsite
anees~ anees$ pythonPython 2710 (default Aug 22 2015 203339)[GCC 421 Compatible Apple LLVM 700 (clang-7000591)] on darwinType help copyright credits or license for more information
Python interpreter is located in usrbin folder and Python standard library filesrarr˓are located in usrlibpython27 folder
anees~ anees$ which pythonusrbinpythonanees~ anees$ ls -l usrlibpython27lrwxr-xr-x 1 root wheel 75 Sep 17 0527 usrlibpython27 -gt SystemLibraryrarr˓FrameworksPythonframeworkVersions27libpython27
anees~ anees$ ls -l usrlibpython27
Skipped -rw-r--r-- 1 root wheel 8252 Aug 22 2037 posixfilepyo-rw-r--r-- 1 root wheel 13925 Aug 22 2036 posixpathpy-rw-r--r-- 1 root wheel 12570 Aug 22 2037 posixpathpyc-rw-r--r-- 1 root wheel 12570 Aug 22 2037 posixpathpyo-rw-r--r-- 1 root wheel 11777 Aug 22 2036 pprintpy-rw-r--r-- 1 root wheel 11144 Aug 22 2037 pprintpyc-rw-r--r-- 1 root wheel 10967 Aug 22 2037 pprintpyo-rwxr-xr-x 1 root wheel 22782 Aug 22 2036 profilepy-rw-r--r-- 1 root wheel 18408 Aug 22 2037 profilepyc-rw-r--r-- 1 root wheel 18161 Aug 22 2037 profilepyo-rw-r--r-- 1 root wheel 26711 Aug 22 2036 pstatspy-rw-r--r-- 1 root wheel 28013 Aug 22 2037 pstatspyc
8 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
-rw-r--r-- 1 root wheel 28013 Aug 22 2037 pstatspyo
Skipped
Ubuntu
Linux distributions come with default Python 27x Some of the newer distributions come with Python 3x Most ofthe linux software modules are built on top of this default Python version So upgrading the default Python versionon Linux will break those modules It is recommended to install the desired version using Python virtua environmentFor the use cases in this book the default Python version should be sufficient
aneesubuntu-web2py~$ which pythonusrbinpythonaneesubuntu-web2py~$aneesubuntu-web2py~$ python --versionPython 276
aneesubuntu-web2py~$ ls -l usrlibpython27total 8228-rw-r--r-- 1 root root 17876 Jun 22 2015 _abcollpy-rw-r--r-- 1 root root 24794 Dec 29 1208 _abcollpyc-rw-r--r-- 1 root root 7145 Jun 22 2015 abcpy-rw-r--r-- 1 root root 6121 Dec 29 1208 abcpyc-rw-r--r-- 1 root root 34231 Jun 22 2015 aifcpy-rw-r--r-- 1 root root 30307 Dec 29 1208 aifcpyc-rw-r--r-- 1 root root 60 Jun 22 2015 antigravitypy-rw-r--r-- 1 root root 201 Dec 29 1208 antigravitypyc-rw-r--r-- 1 root root 2663 Jun 22 2015 anydbmpy-rw-r--r-- 1 root root 2794 Dec 29 1208 anydbmpyc-rw-r--r-- 1 root root 217 Jun 22 2015 argparseegg-info-rw-r--r-- 1 root root 88691 Jun 22 2015 argparsepy-rw-r--r-- 1 root root 63859 Dec 29 1208 argparsepyc-rw-r--r-- 1 root root 11805 Jun 22 2015 astpy-rw-r--r-- 1 root root 12906 Dec 29 1208 astpyc
Skipped
Python Package Manager (pip)
When Python is installed from the source it has come with library of standard modules When a Python interpreteris invoked very limited number of modules were imported by default into your programrsquos main namespace Otherstandard modules in the library can be imported into your program when you need them There are many vendorsdeveloper community create Python modules and deliver them through pip pip is a Python package installer whichis used to install Python packages from a repository called PyPI (Python Package Index) If you download Pythonversions 279 (or 34) and above from wwwpythonorg pip installer is installed by default
If pip is not installed on your Windows or Mac OS X you can download the Python program get-pippy and install iton your system
22 Python Package Manager (pip) 9
arista-python-web2py Documentation Release 001
Listing 21 Microsoft Windows or Mac OS X
python get-pippy
You can verify pip installation on your Windows as described below
Listing 22 Microsoft Windows
CUsersaneesgtpython -m pip showERROR Please provide a package name or namesYou are using pip version 712 however version 802 is availableYou should consider upgrading via the python -m pip install --upgrade pip command
CUsersaneesgtpython -m pip install --upgrade pipCollecting pipDownloading pip-802-py2py3-none-anywhl (12MB)
100 || 12MB 435kBsInstalling collected packages pipFound existing installation pip 712
Uninstalling pip-712Successfully uninstalled pip-712
Successfully installed pip-802
You can verify pip installation on your Mac OS X as described below
Listing 23 Apple Mac OS X
anees~ anees$ pip showERROR Please provide a package name or namesYou are using pip version 712 however version 802 is availableYou should consider upgrading via the pip install --upgrade pip command
anees~ anees$ sudo pip install --upgrade pipPasswordThe directory UsersaneesLibraryCachespiphttp or its parent directory is notrarr˓owned by the current user and the cache has been disabled Please check therarr˓permissions and owner of that directory If executing pip with sudo you may wantrarr˓sudos -H flagThe directory UsersaneesLibraryCachespip or its parent directory is not ownedrarr˓by the current user and caching wheels has been disabled check the permissions andrarr˓owner of that directory If executing pip with sudo you may want sudos -H flagCollecting pip
Downloading pip-802-py2py3-none-anywhl (12MB)100 || 12MB 477kBs
Installing collected packages pipFound existing installation pip 712Uninstalling pip-712
Successfully uninstalled pip-712Successfully installed pip-802anees~ anees$
Since the default Python version on Ubuntu Linux distribution may be prior to 279 you need to install pip fromLinux package manager
10 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
Listing 24 Ubuntu
aneesubuntu-web2py~$ python --versionPython 276
aneesubuntu-web2py~$ sudo apt-get install python-pip
aneesubuntu-web2py~$ pip -Vpip 154 from usrlibpython27dist-packages (python 27)
Later in this book there are few Python packages installed using pip as and when needed by the use cases Some of thepackages may not be delivered through pip and you can download through your system packet manager or manuallydownload it to the library For more information to learn about pip visit httpspippypaioenstable
Installing Python Modules
As mentioned before there are many vendor and community developed modules available and can be installed usingpip In this book we primarily use Aristarsquos pyeapi module We will also use jsonrpc module in some of the use cases inthis book In this section we will install those modules using pip If you have not installed pip refer Python PackageManager (pip)
Pyeapi
[adminubuntu ~]$ sudo pip install pyeapiDownloadingunpacking pyeapi
Downloading pyeapi-061targz (115kB) 115kB downloadedRunning setuppy (pathtmppip_build_rootpyeapisetuppy) egg_info for package
rarr˓pyeapi
Downloadingunpacking netaddr (from pyeapi)Downloading netaddr-0718-py2py3-none-anywhl (15MB) 15MB downloaded
Installing collected packages pyeapi netaddrRunning setuppy install for pyeapi
Successfully installed pyeapi netaddrCleaning up
jsonrpc
anees~ anees$ sudo pip install jsonrpcCollecting jsonrpc
Downloading jsonrpc-12targzInstalling collected packages jsonrpc
Running setuppy install for jsonrpc doneSuccessfully installed jsonrpc-12
23 Installing Python Modules 11
arista-python-web2py Documentation Release 001
Python Core Concepts
In this section we are going to discuss the core Python concepts by using simple network use cases There are sixPython core concepts discussed and it is strongly recommended to practice these concepts as you read You will besurprised how much you can achieve by using these simple concepts as you read through this book
1 Data Type
2 Data Structure
3 Control Flow Statements
4 Data Operations
5 Modules
6 Handling Structured Data
The approach of this book is to learn by practice This is one of the reason we chose to teach Python using networkinguse cases When you practice using the use cases you know we donrsquot necessarily explain every concepts textually Werecommend you to practice these concepts multiple times You also donrsquot restrict yourselves to the use cases discussedin this book Expand the practice with your own networking use cases
Data Type
A couple of data types important to us is strings and integers Launch the Python interpreter from your terminal
anees~ anees$ pythonPython 2710 (default Aug 22 2015 203339)[GCC 421 Compatible Apple LLVM 700 (clang-7000591)] on darwinType help copyright credits or license for more informationgtgtgt
Below are the examples for three data formats string integer and floating point
gtgtgt switch = 10101011gtgtgt type(switch)lttype strgt
gtgtgt last_octet = 11gtgtgt type(last_octet)lttype intgt
gtgtgt temperature = 392gtgtgt type(temperature)lttype floatgt
The line lsquoswitch = ldquo10101011rdquorsquo is called as assignment statement The ldquoswitchrdquo is called as variable and theldquo10101011rdquo is called as value Observe the spaces between variables and values Refer Python Style Guide forwhitespace in expressions for more information
Strings are represented within quotes There are multiple ways to represent strings Below are the examples ofrepresenting strings using single double and triple quotes If you need to type the text in multiple lines as you see inthe variable switch3 you have to use triple quotes
gtgtgt switch1 = description CORE switchgtgtgtgtgtgt switch2 = interface ethernet1
12 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
gtgtgtgtgtgt switch3 = interface ethernet1 ip address 101020224 no shutdown gtgtgt
You can view the content of the variable directly typing the variable name
gtgtgt switch3interface ethernet1n ip address 101020224n no shutdownn
ldquonrdquo is an escape character in Python to represent new line If you need to type the multi line text in single line as yousee above you have to use escape characters within single or double quotes
When you print the content of this string using ldquoprintrdquo module the data is presented in readable format instead ofdisplaying in the string internal format using escape characters
gtgtgt print switch3interface ethernet1ip address 101020224no shutdown
When the data is stored in the system it has to be encoded in a specific format Unicode is an industry standardencoding format We will be often converting unicode to string when we display the data to the end user
gtgtgt switch4 = urouter bgp 100gtgtgtgtgtgt switch4urouter bgp 100gtgtgtgtgtgt str(switch4)router bgp 100gtgtgtgtgtgt switch5 = str(switch4)gtgtgt switch5router bgp 100
Data Structure
Data structure is a way of organizing the data in a system String can be considered as a data structure as well Datastructure helps us to access the data efficiently We are going to see three Python data structures in this section Thoseare list set and dictionary We will use list and dictionary extensively throughout this book
List
List is a Python data structure to store a list of values List is represented using comma-separated values inside asquare [ ] bracket
gtgtgt device_list = [10101013 10101011 10101014 10101012]gtgtgtgtgtgt type(device_list)lttype listgtgtgtgt
24 Python Core Concepts 13
arista-python-web2py Documentation Release 001
gtgtgt device_list[10101013 10101011 10101014 10101012]
How do you access a specific value in the list You can access the value by using the index within the square bracket
gtgtgt device_list[0]10101013gtgtgt device_list[1]10101011gtgtgt device_list[3]10101012
How can we modify the list You can add the items using append() method and you can update the existing item usingassignment statement
gtgtgt device_list[10101011 10101012 10101013]gtgtgtgtgtgt device_listappend(10101012)gtgtgt device_list[10101011 10101012 10101013 10101012]gtgtgtgtgtgt new_ip = 10101015gtgtgt device_listappend(new_ip)gtgtgt device_list[10101011 10101012 10101013 10101012 10101015]gtgtgtgtgtgt device_list[0] = new_ipgtgtgt device_list[10101015 10101012 10101013 10101012 10101015]
As you can see list can have duplicate values Where can I find the list of operations (methods) that can be performedon the list
gtgtgt dir(device_list)[__add__ __class__ __contains__ __delattr__ __delitem__ __delslice__rarr˓ __doc__ __eq__ __format__ __ge__ __getattribute__ __getitem__ __rarr˓getslice__ __gt__ __hash__ __iadd__ __imul__ __init__ __iter__rarr˓__le__ __len__ __lt__ __mul__ __ne__ __new__ __reduce__ __rarr˓reduce_ex__ __repr__ __reversed__ __rmul__ __setattr__ __setitem__rarr˓__setslice__ __sizeof__ __str__ __subclasshook__append count extend index insert pop remove reverse sort]
gtgtgt help(device_list)|| append()| Lappend(object) -- append object to end|| count()| Lcount(value) -gt integer -- return number of occurrences of value|| extend()| Lextend(iterable) -- extend list by appending elements from the iterable|| index()| Lindex(value [start [stop]]) -gt integer -- return first index of value| Raises ValueError if the value is not present|
14 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
| insert()| Linsert(index object) -- insert object before index|| pop()| Lpop([index]) -gt item -- remove and return item at index (default last)| Raises IndexError if list is empty or index is out of range|| remove()| Lremove(value) -- remove first occurrence of value| Raises ValueError if the value is not present|| reverse()| Lreverse() -- reverse IN PLACE|| sort()| Lsort(cmp=None key=None reverse=False) -- stable sort IN PLACE| cmp(x y) -gt -1 0 1ltType q to exitgt
Let us explore few more methods over the list
gtgtgt device_list = [10101011 10101012 10101013 10101014 1010rarr˓1015]
gtgtgt device_listreverse()gtgtgt device_list[10101015 10101014 10101013 10101012 10101011]
gtgtgt device_listsort()gtgtgt device_list[10101011 10101012 10101013 10101014 10101015]
gtgtgt device_listpop()10101015gtgtgt device_list[10101011 10101012 10101013 10101014]
gtgtgt device_listremove(10101012)gtgtgt device_list[10101011 10101013 10101014]
Set
Set is similar to list with few differences
bull Items in set are unique It eliminates duplicate entries
bull Unordered list of items
bull Supports powerful operations such as difference union and intersection between sets
Let us create a set and practice some of the basic operations related to set
gtgtgt device_set = set([10101011 10101012])gtgtgtgtgtgt device_setset([10101011 10101012])
24 Python Core Concepts 15
arista-python-web2py Documentation Release 001
gtgtgt type(device_set)lttype setgt
gtgtgt device_setadd(10101013)gtgtgt device_setset([10101011 10101013 10101012])
gtgtgt device_setremove(10101012)gtgtgt device_setset([10101011 10101013])
gtgtgt new_ip = 10101013gtgtgt device_setadd(new_ip)gtgtgt device_setset([10101011 10101013])
You can convert a list to set If the list has duplicate entries converting to set will eliminate the duplicate entries
gtgtgt device_list[10101015 10101012 10101013 10101012 10101015]
gtgtgt device_list_to_set = set(device_list)gtgtgt device_list_to_setset([10101015 10101013 10101012])
Let us explore the methods specific to set
gtgtgt dir(device_set)[__and__ __class__ __cmp__ __contains__ __delattr__ __doc__ __eq__rarr˓ __format__ __ge__ __getattribute__ __gt__ __hash__ __iand__ __rarr˓init__ __ior__ __isub__ __iter__ __ixor__ __le__ __len__ __lt__rarr˓ __ne__ __new__ __or__ __rand__ __reduce__ __reduce_ex__ __rarr˓repr__ __ror__ __rsub__ __rxor__ __setattr__ __sizeof__ __str__rarr˓__sub__ __subclasshook__ __xor__add clear copy difference difference_update discard intersectionrarr˓intersection_update isdisjoint issubset issuperset pop removerarr˓symmetric_difference symmetric_difference_update union update]
gtgtgt switch1_neighbors = set([switch2 switch3 switch4])gtgtgt switch2_neighbors = set([switch1 switch3 switch5])
gtgtgt switch1_neighborsintersection(switch2_neighbors)set([switch3])
gtgtgt switch1_neighborsunion(switch2_neighbors)set([switch3 switch2 switch1 switch5 switch4])
gtgtgt switch2_neighborsdifference(switch1_neighbors)set([switch1 switch5])
Dictionary
Dictionary is a list of key value items and defined using curly brackets Let us look at the basic operations usingdictionary
16 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
gtgtgt inventory = 10101011 spine-1 10101012 spine-2gtgtgt type(inventory)lttype dictgt
gtgtgt inventory[10101011]spine-1
gtgtgt inventory[10101013] = tor-1gtgtgt inventory10101011 spine-1 10101013 tor-1 10101012 spine-2
gtgtgt inventorykeys()[10101011 10101013 10101012]
The values of the dictionary can be a simple string a list or another dictionary Below is an example that shows a valueof a key which is another dictionary
gtgtgt inventory[10101011] = hostname spine-1 version 4154F modelrarr˓7050SX-128gtgtgt inventory10101011 model 7050SX-128 hostname spine-1 version 4154Frarr˓10101013 tor-1-a 10101012 spine-2
gtgtgt inventory[10101011]model 7050SX-128 hostname spine-1 version 4154F
gtgtgt inventory[10101011][version]4154F
Control Flow Statements
The flow of the script can be changed by conditional and loop statements We are going to take a look at ldquoif clauserdquoand ldquofor looprdquo in this section
if
Here is an example of using simple if clause We are also using the operators == (Equal to) and = (Not Equal to)
gtgtgt interface = management1gtgtgtgtgtgt if interface == management1 print It is the management interfaceIt is the management interface
gtgtgt if interface = management1 print It is NOT the management interfacegtgtgt
The statements following if statement are indented by 4 spaces Refer Python Style Guide for Indentation for moreinformation Here is an example of using if and else clause We are using the operator lt= (Less than equal to)
gtgtgt max_interfaces = 36
gtgtgt n = 10
24 Python Core Concepts 17
arista-python-web2py Documentation Release 001
gtgtgt if n lt= max_interfaces print Interface number is within the range else print Interface number is not within the rangeInterface number is within the range
Here is an example of using if elif and else clause We are using the operator ldquonot inrdquo against the list
gtgtgt device_list = [10101011 10101012 10101013]
gtgtgt if 10101011 not in device_list print 10101011 is not in the list elif 10101012 not in device_list print 10101012 is not in the list elif 10101013 not in device_list print 10101013 not in device_list else print all the IPs are in the device_listall the IPs are in the device_list
Here is an example to find whether a list is empty or not using if clause
gtgtgt device_list = []gtgtgt if not device_list print Empty ListEmpty List
gtgtgt device_list = [10101011]gtgtgt if not device_list print Empty List else print Not EmptyNot Empty
Here is an example to find whether a dictionary is empty or not using if clause
gtgtgt inventory = 10101011 host1
gtgtgt not inventoryFalsegtgtgtgtgtgt bool(inventory)True
gtgtgt if bool(inventory) print inventory10101011 host1
gtgtgt if not inventory print Empty dictionary else print inventory
18 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
10101011 host1
With Empty Dictionary
gtgtgt inventory =
gtgtgt not inventoryTruegtgtgt bool(inventory)False
gtgtgt if not inventory print Empty dictionaryEmpty dictionary
gtgtgt if bool(inventory) print NOT EMPTY else print Empty DictionaryEmpty Dictionary
for
for loop is used to iterate over a sequence of items Below is an example of iterating list and dictionary
gtgtgt device_list = [10101011 10101012 10101013]gtgtgtgtgtgt for each_ip in device_list print each_ip101010111010101210101013
gtgtgt inventory = 10101011 model 7050SX-128 hostname spine-1rarr˓version 4154F 10101012 model 7050SX-128 hostname spine-2rarr˓ version 4154F 10101013 model 7050SX-128 hostname leaf-1rarr˓ version 4156M
gtgtgt for each_ip in inventory print each_ip101010111010101310101012
gtgtgt for each_ip in inventory print inventory[each_ip][hostname]spine-1leaf-1spine-2
24 Python Core Concepts 19
arista-python-web2py Documentation Release 001
Data Operations
In this section we are going to discuss about the various in built methods for strings and integers
String
By now you know how to find the supported methods (dir()) for various types of data structures and data types Let uspractice some basic methods on strings
gtgtgt ip_address = ip address 1010101124gtgtgt ip_addresssplit()[ip address 1010101124]
gtgtgt ip_addresssplit()[2]1010101124
gtgtgt ip_addresssplit()[2]split()[10101011 24]
gtgtgt ip_addresssplit()[2]split()[0]10101011
split() splits the string into list The default delimiter is empty space
Now let us practice how we can combine strings
gtgtgt ip = 101010100gtgtgt subnet_mask = 24
gtgtgt ip_addr = ip address + ipgtgtgt ip_addrip address101010100
gtgtgt ip_addr = ip address + ipgtgtgt ip_addrip address 101010100
gtgtgt ip_statement = ip_addr + + subnet_maskTraceback (most recent call last)
File ltstdingt line 1 in ltmodulegtTypeError cannot concatenate str and int objects
gtgtgt type(subnet_mask)lttype intgt
gtgtgt ip_statement = ip_addr + + str(subnet_mask)gtgtgt ip_statementip address 10101010024
As you see when we try to add a string and integer Python reports TypeError cannot concatenate lsquostrrsquo and lsquointrsquoobjects So we converted the integer to string using str(subnet_mask) method and concatenated to ip_addr stringvariable
Integer
Here are the some of the examples of methods over integers and floating point
20 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
gtgtgt number_of_line_cards = 8gtgtgt ports_per_line_card = 36
gtgtgt total_number_of_ports = number_of_line_cards ports_per_line_cardgtgtgt total_number_of_ports288
gtgtgt used_ports = 120gtgtgt free_ports = total_number_of_ports - used_portsgtgtgt free_ports168
gtgtgt ports_usage_in_percentage = (used_portstotal_number_of_ports) 100gtgtgt ports_usage_in_percentage0
gtgtgt 1202880gtgtgt float(120288)00gtgtgt 1200288004166666666666667
gtgtgt ports_usage_in_percentage = (float(used_ports)float(total_number_of_ports) 100)gtgtgt ports_usage_in_percentage4166666666666667
ldquofree_ports = total_number_of_ports - used_portsrdquo is called as statement In that ldquototal_number_of_ports -used_portsrdquo is called as expression Within the expression total_number_of_ports used_ports are variables andldquo-rdquo is called as operator
Simple Use Cases
We will practice few simple use cases based on the concepts we have learned so far Here is an example of using forloop and string methods In this example we will extract the IP addresses from the ldquoshow ip interface briefrdquo output
gtgtgt ip_int_br = Interface IP Address Status Protocolrarr˓ MTU Ethernet11101 192168121031 up up 1500 Ethernet21101 192168221031 down lowerlayerdown 1500 Ethernet31301 10121031 up up 1500 Ethernet31317 101213231 up up 1500 Ethernet31333 101216431 up up 1500 Ethernet31349 101219631 up up 1500
gtgtgt ip_int_br Interface IP Address Status Protocolrarr˓MTUnEthernet11101 192168121031 up uprarr˓1500nEthernet21101 192168221031 down lowerlayerdownrarr˓1500nEthernet31301 10121031 up uprarr˓1500nEthernet31317 101213231 up uprarr˓1500nEthernet31333 101216431 up uprarr˓1500nEthernet31349 101219631 up up 1500
gtgtgt ip_int_brsplit(n)[ Interface IP Address Status Protocol MTUrarr˓Ethernet11101 192168121031 up up 1500rarr˓Ethernet21101 192168221031 down lowerlayerdown 1500rarr˓Ethernet31301 10121031 up up 1500rarr˓Ethernet31317 101213231 up up 1500rarr˓Ethernet31333 101216431 up up 1500rarr˓Ethernet31349 101219631 up up 1500]
24 Python Core Concepts 21
arista-python-web2py Documentation Release 001
gtgtgt for each_line in ip_int_brsplit(n) print each_lineInterface IP Address Status Protocol MTU
Ethernet11101 192168121031 up up 1500Ethernet21101 192168221031 down lowerlayerdown 1500Ethernet31301 10121031 up up 1500Ethernet31317 101213231 up up 1500Ethernet31333 101216431 up up 1500Ethernet31349 101219631 up up 1500
gtgtgt for each_line in ip_int_brsplit(n) print each_linesplit()[1]IP19216812103119216822103110121031101213231101216431101219631
Here is another example of using for loop string and integer methods We will calculate the number of subnets and IPaddresses (including network and broadcast IP addresses) from the given network address and subnet mask
gtgtgt network_block = 1921681024gtgtgt subnet_mask = 30
Identify the number of bits used for network subnet_mask - network_mask gtgtgt network_blocksplit()[19216810 24]gtgtgt network_blocksplit()[1]24gtgtgt subnet_mask - int(network_blocksplit()[1])6
Number of subnets = 2 to the power of (subnet_mask - network_mask) gtgtgt number_of_subnets = 2 (subnet_mask - int(network_blocksplit()[1]))gtgtgt number_of_subnets64
Number of IPs per subnet = 2 to the power of number of host bits gtgtgt number_of_ips_per_subnet = 2 (32 - subnet_mask)gtgtgt number_of_ips_per_subnet4
If you look at some of the statements we have more than one operators in the expression When you have more thanone operators in an expression Python follows the conventional method called as PEMDAS (Parenthesis ExponentsMultiplication Division Addition Subtraction) to calculate the result
Now we will continue to update our script to calculate the subnet addresses for the given network block and subnetmask
gtgtgt subnet_octets = network_blocksplit()[0]split()gtgtgt subnet_octets[192 168 1 0]
22 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
gtgtgt join(subnet_octets)19216810
gtgtgt subnetwork_address = int(subnet_octets[3])gtgtgt for each_subnet in range(0 number_of_subnets) subnet_octets[3] = str(subnetwork_address) subnets = join(subnet_octets) + + str(subnet_mask) print subnets subnetwork_address += number_of_ips_per_subnet1921681030192168143019216818301921681123019216811630 Output Omitted for brevity192168122830192168123230192168123630192168124030192168124430192168124830192168125230
Within the for loop we are just updating the fourth octet of the IP addresses and appending the string to list the subnetaddresses
Script File
So far We have been practicing the core concepts using Python interpreter This approach of using interpreter is calledas interactive mode As you see in the previous use case it is getting complicated to use the interpreter as the scriptgets complicated It is time to start writing scripts in a file which will be saved as py file in your system Then thescript file is executed directly from your terminal or from any development environment Now the question comeswhat editor should we use to create the py file One could use a simple text editor to write the scripts Working withthe text editor is something similar to writing script in the Python interpreter where you have to make sure you writethe code with correct indentation and correct syntax
There are sophisticated advanced editors that can provide auto indentation and in some cases auto completing thestatements You can start writing the script using one of the advanced editors such as Atom or Sublime
Later in this book we use Pythonrsquos Integrated Development Environment (IDLE) to create scripts IDLE is installed aspart of the Python software package when you download and install from wwwpythonorg There are several vendordeveloped integrated development environments (IDE) such as Wing IDE PyCharm Komodo etc are available inthe market In addition to the benefits provided by advanced editors IDEs are great while developing testing anddebugging complex software applications
Create a folder in the home directory of your system and save the scripts you are writing in that folder Open yourchoice of editor and create a new script and save the file as subnet_calculatorpy
We are going to take the script we built so far using interactive mode and paste it in this new script file The script wehave written so far is very specific to class C subnets We are going to add a logic that automatically derive the classfrom the given network block and subnet mask
network_block = 103210024subnet_mask = 26
split the network block into IP address and Network Mask
24 Python Core Concepts 23
arista-python-web2py Documentation Release 001
ip_address = network_blocksplit()[0]network_mask = int(network_blocksplit()[1])octets = ip_addresssplit()
Find the octet number for which subnet address needs to be calculated class A = 1 Class B = 2 Class C = 3subnet_octet = network_mask 8
Find out how many subnets for a given network mask and subnet masknumber_of_subnets = 2 (subnet_mask - network_mask)
Total number of IPs within a subnet host class for class A B and C is 16 24 and 32 respectivelyhost_class = (subnet_octet 8) + 8number_of_ips_per_subnet = 2 (host_class - subnet_mask)
Derive the subnet addressessubnetwork_address = int(octets[subnet_octet])
for each_subnet in range(0 number_of_subnets)octets[subnet_octet] = str(subnetwork_address)subnets = join(octets) + + str(subnet_mask)print subnetssubnetwork_address += number_of_ips_per_subnet
It may take sometime to understand the logic of the script Later in this book we will learn to write an algorithm andwe develop all the scripts step by step by following the algorithm For now just save and run the script from yoursystem
aneesmy-scripts anees$ ls subnetsubnet_calculatorpy
aneesmy-scripts anees$ python subnet_calculatorpy10321002610321064261032101282610321019226
Modules
As you start learning to write scripts you will end up in reusing some of your programs You can write the reusablescripts as functions in Python You can also write all of your reusable use cases as multiple functions and save it in apy file Then for any new programs or scripts you can import these functions in the py file and use it This py file iscalled as module in Python
In some cases you may end up in building multiple modules and you may need all these modules to build moresophisticated software applications Collection of these modules can be called as packages in Python
There are several standard modules or packages installed as part of the Python installation You can find those packagesin the lib folder of Python installation in your system For example in Windows cpython27lib and in Linux amp MACOS X usrlibpython27 have the standard Python modules
We will explore some of the standard modules (pprint sys and re) in this section Launch the Python interpreter fromyour terminal
24 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
pprint
Pretty printer (pprint) is a very useful module especially when we handle dictionaries In order to use the modulesyou must import them into your script
gtgtgt import pprintgtgtgtgtgtgt dir()[__builtins__ __doc__ __name__ __package__ pprint]gtgtgt dir(pprint)[PrettyPrinter _StringIO __all__ __builtins__ __doc__ __file__ __rarr˓name__ __package__ _commajoin _id _len _perfcheck _recursion _rarr˓safe_repr _sorted _sys _typeisreadable isrecursive pformat pprint saferepr warnings]
gtgtgt inventory = 10101011 model 7050SX-128 hostname spine-1rarr˓version 4154F 10101013 tor-1-a 10101012 spine-2
gtgtgt print inventory10101011 model 7050SX-128 hostname spine-1 version 4154Frarr˓10101013 tor-1-a 10101012 spine-2
gtgtgt pprintpprint(inventory)10101011 hostname spine-1
model 7050SX-128version 4154F
10101012 spine-210101013 tor-1-a
Both print and pprint are inbuilt modules in Python But print is loaded in your program namespace when you launchthe Python program by default You can see the difference between the outputs of the dictionary using print and pprint
sys
sys module provide system specific functions which can be used to interact with the systems (Microsoft WindowsApple Mac Linux etc) objects and variables In the subnet_calculatorpy we specify the network address and thesubnet mask within the script We are going to use sys module to input both of these values as arguments instead ofhard coding into the script
import sys
network_block = sysargv[1]subnet_mask = int(sysargv[2])
split the network block into IP address and Network Maskip_address = network_blocksplit()[0]network_mask = int(network_blocksplit()[1])octets = ip_addresssplit()
Find the octet number for which subnet address needs to be calculatedsubnet_octet = network_mask 8
Find out how many subnets for a given network mask and subnet masknumber_of_subnets = 2 (subnet_mask - network_mask)
Total number of IPs within a subnethost_class = (subnet_octet 8) + 8
24 Python Core Concepts 25
arista-python-web2py Documentation Release 001
number_of_ips_per_subnet = 2 (host_class - subnet_mask)
Derive the subnet addressessubnetwork_address = int(octets[subnet_octet])
for each_subnet in range(0 number_of_subnets)octets[subnet_octet] = str(subnetwork_address)subnets = join(octets) + + str(subnet_mask)print subnetssubnetwork_address += number_of_ips_per_subnet
Save and run the script from your system
aneesmy-scripts anees$ ls subnetsubnet_calculatorpy
aneesmy-scripts anees$ python subnet_calculatorpy 101010024 2610101002610101064261010101282610101019226
What will happen if the user does not provide the arguments
aneesmy-scripts anees$ python subnet_calculatorpyTraceback (most recent call last)
File subnet_calculatorpy line 3 in ltmodulegtnetwork_block = sysargv[1]
IndexError list index out of range
We can add a validation check in the script to make sure the user provides two arguments If the user does not providethe arguments the script should display a message that educates the user
import sys
if len( sysargv ) lt= 2sysstderrwrite(Example Syntax n)sysstderrwrite(python subnet_calculatorpy 1921681024 28 n)sysexit( 1 )
network_block = sysargv[1]subnet_mask = int(sysargv[2])
split the network block into IP address and Network Maskip_address = network_blocksplit()[0]network_mask = int(network_blocksplit()[1])octets = ip_addresssplit()
Find which octet for which subnet address needs to be calculated class A = 1 Class B = 2 Class C = 3subnet_octet = network_mask 8
Find out how many subnets for a given network mask and subnet masknumber_of_subnets = 2 (subnet_mask - network_mask)
Total number of IPs within a subnet host class for class A B and C is 16 24 and 32host_class = (subnet_octet 8) + 8
26 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
number_of_ips_per_subnet = 2 (host_class - subnet_mask)
Derive the subnet addressessubnetwork_address = int(octets[subnet_octet])
for each_subnet in range(0 number_of_subnets)octets[subnet_octet] = str(subnetwork_address)subnets = join(octets) + + str(subnet_mask)print subnetssubnetwork_address += number_of_ips_per_subnet
Save and run the script from your system
aneesmy-scripts anees$ python subnet_calculatorpyExample Syntaxpython subnet_calculatorpy 1921681024 28
re - Regular Expression
Regular expression is a powerful method when it comes to automation We are going to practice a couple of use casesin this section using regular expression
Best Practices Assessment
In this first use case we are going to use research() method We are going to practice a script to validate if the bgpbest practices commands such as ldquoupdate wait-for-convergencerdquo and ldquoupdate wait-installrdquo are configured
gtgtgt import regtgtgtgtgtgt config = interface Loopback0 description loopback interface for BGP peering ip address 192168100232 router bgp 100 update wait-for-convergence maximum-paths 4 neighbor 10111 remote-as 1001 neighbor 10111 maximum-routes 12000 neighbor 19216812818 remote-as 201 neighbor 19216812818 maximum-routes 12000 neighbor 19216812822 remote-as 201 neighbor 19216812822 maximum-routes 12000 network 101010024 network 192168100232 gtgtgtgtgtgt validate = research(update wait-for-convergence config)gtgtgt validatelt_sreSRE_Match object at 0x100f5d9f0gt
gtgtgt if validate print Match Found else print Match NOT FoundMatch Found
24 Python Core Concepts 27
arista-python-web2py Documentation Release 001
gtgtgtgtgtgt validate = research(update wait-install config)gtgtgt validategtgtgtgtgtgt if validate print Match Found else print Match NOT FoundMatch NOT Found
If a match is found research() will return a Match Object instance If a match is not found it returns None
Configuration Generator - Collecting the variables from the configuration template
In this second use case we are going to use refindall() method Network engineers can standardize network deviceconfigurations Once they standardize network device configurations they can create configuration template whichcan be used to generate configurations for any number of devices by filling up the variables such as hostname IPaddresses etc In this example we will show how we can collect the variables from the configuration template Laterin this chapter we will complete the script by replacing the variables with the actual values
Import re
gtgtgt config = hostname $hostname interface management1 ip address $ip_mgmt24 interface loopback0 ip address $ip_lo032
gtgtgt refindall($w+ config)[$hostname $ip_mgmt $ip_lo0]
Handling Structured Data
In this section we are going to complete the configuration generator script which we started in the regular expressionsection We are going to save the configuration template in a text file and the variables will be stored in a json or yamlfile
Text
Create a configuration template and store it in a text file
aneesfiles anees$ pwdUsersaneesDropboxmy-scriptsfiles
aneesfiles anees$ vi config_templatetxthostname $hostnameinterface management1ip address $ip_mgmt24
28 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
interface loopback0ip address $ip_lo032
interface ethernet491speed forced 40gfullip address $ip_to_spine131
interface ethernet501speed forced 40gfullip address $ip_to_spine231
router bgp $bgp_asrouter-id $ip_lo0neighbor $ip_spine1 remote-as 65000neighbor $ip_spine2 remote-as 65000network $ip_lo032
We will write a Python script to read this file Create a Python script file and call it as config_generatorpy withopen(ldquofile_namerdquo) is used to open the file It automatically closes the file after the indented code block is executed
file_path = UsersaneesDropboxmy-scriptsfilesfile_name = config_templatetxt
config_file = file_path + file_name
with open(config_file) as read_fileconfig_template = read_fileread()
print config_template
Save and run the script
aneesmy-scripts anees$ python config_generatorpyhostname $hostnameinterface management1ip address $ip_mgmt24
interface loopback0ip address $ip_lo032
interface ethernet491speed forced 40gfullip address $ip_to_spine131
interface ethernet501speed forced 40gfullip address $ip_to_spine231
router bgp $bgp_asrouter-id $ip_lo0neighbor $ip_spine1 remote-as 65000neighbor $ip_spine2 remote-as 65000network $ip_lo032
Now we are going to identify all the variables in the configuration template We can use the regular expression module
24 Python Core Concepts 29
arista-python-web2py Documentation Release 001
to identify all the variables starting with $ refindall(ldquo$w+rdquo config_template) creates a list of the words starting with$ Since the configuration template may use the same variable name in multiple places there will be duplicate entriesin the list created by refindall We will convert the list to set to eliminate the duplicate entries
import re
file_path = UsersaneesDropboxmy-scriptsfilesfile_name = config_templatetxt
config_file = file_path + file_name
with open(config_file) as read_fileconfig_template = read_fileread()
variables = set(refindall($w+ config_template))
print variables
Save and run the script
aneesmy-scripts anees$ python config_generatorpyset([$ip_mgmt $ip_lo0 $hostname $ip_spine2 $ip_spine1 $ip_to_spine2rarr˓$ip_to_spine1 $bgp_as])
JSON
JSON provides a data structure which is easy to read by human as well as easy to parse the data using programminglanguages The structure is very similar to what we learned in Python dictionary (key value) earlier in this chapterWe will create a file using JSON format with the variables we have used in the configuration template as keys
aneesmy-scripts anees$ vi variablesjson
$ip_mgmt 192168111$ip_lo0 10101010$hostname sjcpod1tor1$ip_spine2 10101012$ip_spine1 10101022$ip_to_spine2 10101011$ip_to_spine1 10101021$bgp_as 65101
We will update our config_generatorpy to read this json file and replace the variables in the configuration templatewith the values in the variablesjson file In order to read the json file we will use the Python json module
import reimport json
file_path = UsersaneesDropboxmy-scriptsfilesfile_name = config_templatetxtvariables_file_name = variablesjson
config_file = file_path + file_namevariables_file = file_path + variables_file_name
Read Config template filewith open(config_file) as read_file
30 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
config_template = read_fileread()
Read the variables from the config templatevariables = set(refindall($w+ config_template))
Read the variables from the json filewith open(variables_file) as read_file
variables_dictionary = jsonload(read_file)
print variables_dictionary
Save and run the script
aneesmy-scripts anees$ python config_generatorpyu$ip_mgmt u192168111 u$ip_lo0 u10101010 u$hostname usjcpod1tor1rarr˓ u$ip_spine2 u10101012 u$ip_spine1 u10101022 u$ip_to_spine2 urarr˓10101011 u$ip_to_spine1 u10101021 u$bgp_as u65101
As you can see jsonload(read_file) imports the content of the json file as dictionary Now we will update our scriptwhich generates the configuration from the configuration template and the variables dictionary
import reimport json
file_path = UsersaneesDropboxmy-scriptsfilesfile_name = config_templatetxtvariables_file_name = variablesjson
config_file = file_path + file_namevariables_file = file_path + variables_file_name
Read Config template filewith open(config_file) as read_file
config_template = read_fileread()
Read the variables from the config templatevariables = set(refindall($w+ config_template))
Read the variables from the json filewith open(variables_file) as read_file
variables_dictionary = jsonload(read_file)
Generate Configuration from Config template and variablesconfig = config_template
for each_variable in variablesconfig = configreplace(each_variable variables_dictionary[each_variable])
print config
Save and run the script
aneesmy-scripts anees$ python config_generatorpyhostname sjcpod1tor1interface management1ip address 19216811124
24 Python Core Concepts 31
arista-python-web2py Documentation Release 001
interface loopback0ip address 1010101032
interface ethernet491speed forced 40gfullip address 1010102131
interface ethernet501speed forced 40gfullip address 1010101131
router bgp 65101router-id 10101010neighbor 10101022 remote-as 65000neighbor 10101012 remote-as 65000network 1010101032
YAML
YAML is a data serialization language which provides a human readable data structure that can be easily accessibleby programming languages YAML is a superset of JSON
In the previous section we created the variables file in JSON format In this section we will create the variables inYAML format We are going to create the variables for multiple devices
aneesmy-scripts anees$ vi variablesyaml---
JPE14080457$ip_mgmt 192168111$ip_lo0 10101011$hostname sjcpod1tor1$ip_spine2 10101011$ip_spine1 10101021$ip_to_spine2 10101010$ip_to_spine1 10101020$bgp_as 65101
JPE14421537$ip_mgmt 192168112$ip_lo0 10101012$hostname sjcpod1tor2$ip_spine2 10101013$ip_spine1 10101023$ip_to_spine2 10101012$ip_to_spine1 10101022$bgp_as 65102
We will update our config_generatorpy script to read the yaml file and display the content We will use pprint moduleto print the output
import reimport yamlimport pprint
file_path = UsersaneesDropboxmy-scriptsfilesfile_name = config_templatetxt
32 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
variables_file_name = variablesyaml
config_file = file_path + file_namevariables_file = file_path + variables_file_name
Read Config template filewith open(config_file) as read_file
config_template = read_fileread()
Read the variables from the config templatevariables = set(refindall($w+ config_template))
Read the variables from the json filewith open(variables_file) as read_file
variables_dictionary = yamlload(read_file)
pprintpprint(variables_dictionary)
Save and run the script
aneesmy-scripts anees$ python config_generatorpyJPE14080457 $bgp_as 65101
$hostname sjcpod1tor1$ip_lo0 10101011$ip_mgmt 192168111$ip_spine1 10101021$ip_spine2 10101011$ip_to_spine1 10101020$ip_to_spine2 10101010
JPE14421537 $bgp_as 65102$hostname sjcpod1tor2$ip_lo0 10101012$ip_mgmt 192168112$ip_spine1 10101023$ip_spine2 10101013$ip_to_spine1 10101022$ip_to_spine2 10101012
Now we will update our script which generates the configuration from the configuration template and the variablesdictionary
import reimport yaml
file_path = UsersaneesDropboxmy-scriptsfilesfile_name = config_templatetxtvariables_file_name = variablesyaml
config_file = file_path + file_namevariables_file = file_path + variables_file_name
Read Config template filewith open(config_file) as read_file
config_template = read_fileread()
Read the variables from the config templatevariables = set(refindall($w+ config_template))
24 Python Core Concepts 33
arista-python-web2py Documentation Release 001
Read the variables from the json filewith open(variables_file) as read_file
variables_dictionary = yamlload(read_file)
Generate Configuration from Config template and variablesfor each_switch in variables_dictionary
config = config_templatefor each_variable in variables
config = configreplace(each_variable str(variables_dictionary[each_rarr˓switch][each_variable]))
print 50print config
Save and run the script
aneesmy-scripts anees$ python config_generatorpyhostname sjcpod1tor2interface management1ip address 19216811224
interface loopback0ip address 1010101232
interface ethernet491speed forced 40gfullip address 1010102231
interface ethernet501speed forced 40gfullip address 1010101231
router bgp 65102router-id 10101012neighbor 10101023 remote-as 65000neighbor 10101013 remote-as 65000network 1010101232
hostname sjcpod1tor1interface management1ip address 19216811124
interface loopback0ip address 1010101132
interface ethernet491speed forced 40gfullip address 1010102031
interface ethernet501speed forced 40gfullip address 1010101031
router bgp 65101
34 Chapter 2 Chapter 1 Introducing Python Core Concepts
arista-python-web2py Documentation Release 001
router-id 10101011neighbor 10101021 remote-as 65000neighbor 10101011 remote-as 65000network 1010101132
aneesmy-scripts anees$
Summary
Since we learned the core Python concepts we will move forward to build scripts for Arista networking use cases Inthe next chapter we will build a framework which you can use to build Python scripts for networking use cases Weare going to use Aristarsquos Pyeapi module to interact with Arista switches We have also used jsonrpc for some of theuse cases Before proceeding to next chapter install pyeapi module using pip on your system
25 Summary 35
arista-python-web2py Documentation Release 001
36 Chapter 2 Chapter 1 Introducing Python Core Concepts
CHAPTER 3
Chapter 2 Script Framework for Use Cases
bull First Script - Show version
bull Second Script - Running Show Version on Multiple Switches
ndash Using IDLE
ndash for loop
bull Third Script - Using External Input
bull Fourth Script ndash Storing Result in a Dictionary
bull Fifth Script ndash Handling Exceptions
bull Summary ndash Script Framework
This chapter is going to help you to build a Python script framework using some of the core concepts learned in theprevious chapter By using this framework you build some of the common network operational use cases such asinventory capacity planning and troubleshooting network issues later in this book
First Script - Show version
In this section we will write a Python script using pyeapi and connect to one of the Arista switches and collect ldquoshowversionrdquo from the switch
Make sure you enable management api on the Arista switch Below is the sample configuration to enable managementapi when the management interface is configured under default vrf
configureinterface Management1
ip address 172281324220
37
arista-python-web2py Documentation Release 001
management api http-commandsno shutdown
Below is the sample configuration to enable management api when the management interface is configured under nondefault vrf
configureinterface Management1
vrf forwarding mgmtip address 172281324520
management api http-commands
no shutdownvrf mgmt
no shutdown
Launch the Python interpreter from your system and connect to Arista switch using pyeapi If you have not installedpyeapi module refer Installing Python Modules
anees~ anees$ pythonPython 2710 (default Aug 22 2015 203339)[GCC 421 Compatible Apple LLVM 700 (clang-7000591)] on darwinType help copyright credits or license for more information
gtgtgt import pyeapi
gtgtgt node = pyeapiconnect(transport=https host=1722813241 username=adminrarr˓password=admin port=None)
gtgtgt version = nodeexecute([show version])
gtgtgt versionujsonrpc u20 uresult [umemTotal 8069504 uversion u4152F urarr˓internalVersion u4152F-26634444152F userialNumber uJPE14080457 urarr˓systemMacAddress u001c73574f49 ubootupTimestamp 14466770122 urarr˓memFree 4381616 umodelName uDCS-7050SX-128-F uarchitecture ui386 urarr˓internalBuildId ub664b979-69d7-4157-9543-20278086874a uhardwareRevision urarr˓0200] uid u4522839056
Response of the output is stored as dictionary in the variable ldquoversionrdquo Dictionary is a Python data structure repre-sented in bracket and the data is represented as keyltvaluegt pair
gtgtgt type(version)lttype dictgt
gtgtgt versionkeys()[ujsonrpc uresult uid]
gtgtgt version[result][umemTotal 8069504 uversion u4152F uinternalVersion u4152F-2663444rarr˓4152F userialNumber uJPE14080457 usystemMacAddress u001c73574f49rarr˓ubootupTimestamp 14466770122 umemFree 4381616 umodelName uDCS-7050SX-rarr˓128-F uarchitecture ui386 uinternalBuildId ub664b979-69d7-4157-9543-rarr˓20278086874a uhardwareRevision u0200]
Value of the key ldquoresultrdquo is a list which is another Python data structure represents in square [ ] brackets
38 Chapter 3 Chapter 2 Script Framework for Use Cases
arista-python-web2py Documentation Release 001
gtgtgt type(version[result])lttype listgtgtgtgtgtgtgt len(version[result])1gtgtgt version[result][0]umemTotal 8069504 uversion u4152F uinternalVersion u4152F-2663444rarr˓4152F userialNumber uJPE14080457 usystemMacAddress u001c73574f49rarr˓ubootupTimestamp 14466770122 umemFree 4381616 umodelName uDCS-7050SX-rarr˓128-F uarchitecture ui386 uinternalBuildId ub664b979-69d7-4157-9543-rarr˓20278086874a uhardwareRevision u0200
Finally the desired data is accessible in the output of version[ldquoresultrdquo][0] As you see the curly bracket it is a Pythondictionary data structure You can access the desired data using the keys ldquoversionrdquo ldquomodelNamerdquo or ldquoserialNumberrdquo
gtgtgt version[result][0][version]u4152F
gtgtgt Data is represented in unicode format ursquo rsquo You can convert to string usingrarr˓str()
gtgtgt str(version[result][0][version])4152F
gtgtgt str(version[result][0][serialNumber])JPE14080457
gtgtgt str(version[result][0][modelName])DCS-7050SX-128-F
As you have seen the actual output of show version is stored as dictionary under List which is a value within a parentdictionary If it is confusing Pythonrsquos pprint module provides a good view of the nested data structure
gtgtgt import pprint
gtgtgt pprintpprint(version)uid u4522839056ujsonrpc u20uresult [uarchitecture ui386
ubootupTimestamp 14466770122uhardwareRevision u0200uinternalBuildId ub664b979-69d7-4157-9543-20278086874auinternalVersion u4152F-26634444152FumemFree 4381616umemTotal 8069504umodelName uDCS-7050SX-128-FuserialNumber uJPE14080457usystemMacAddress u001c73574f49uversion u4152F]
gtgtgt version[result][0][serialNumber]uJPE14080457
31 First Script - Show version 39
arista-python-web2py Documentation Release 001
Second Script - Running Show Version on Multiple Switches
Power of scriptming language is repeatability Since you have already created a script to collect show version let ususe this code to collect show version from multiple switches in the network
Since we will be creating several python scripts let us create a folder in our systems In this example I have created afolder called my-scripts under UsersaneesGoogle Drive
Using IDLE
Now it is the time to start using Pythonrsquos IDLE environment This is helpful while developing the script where wehave to test the script as we make progress with the script
For MAC type ldquoidlerdquo in the terminal window and you will see Python IDLE shell is opened
Create a new file from File rarr New File
40 Chapter 3 Chapter 2 Script Framework for Use Cases
arista-python-web2py Documentation Release 001
It opens a new untitled IDLE file Save this file using File rarr Save As under the folder you created with the name asinventory_versionpy
Write your script and save from File rarr Save (or Command + S)
import pyeapi
node = pyeapiconnect(transport=https host=1722813241 username=adminrarr˓password=admin port=None)
version = nodeexecute([show version])
Test your script from Run rarr Run Module (or F5)
32 Second Script - Running Show Version on Multiple Switches 41
arista-python-web2py Documentation Release 001
The script runs in the IDLE shell which you can see in the right side of the following screenshot You can also accessthe variables from the IDLE shell
for loop
Now let us create a script to collect the Model Name Serial Number and EOS versions on multiple Arista switches inyour network
import pyeapi
Create a List of Switchesswitches = [1722813241 1722813240 1722813245]
for switch in switches Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=admin password=
rarr˓admin port=None)
Execute the desired commandversion = nodeexecute([show version])
Print the desired Outputprintprint ()print (Switch IP s) (switch)print (Model Name s) (version[result][0][modelName])print (Serial Number s) (version[result][0][serialNumber])print (EOS Version s) (version[result][0][version])print ()print
Save and run the command You will see the output in the IDLE shell similar to the following output
gtgtgt ================================ RESTART ================================gtgtgt
42 Chapter 3 Chapter 2 Script Framework for Use Cases
arista-python-web2py Documentation Release 001
Switch IP 1722813241Model Name DCS-7050SX-128-FSerial Number JPE14080457EOS Version 4152F
Switch IP 1722813240Model Name DCS-7050SX-128-FSerial Number JPE14080459EOS Version 4152F
Switch IP 1722813245Model Name DCS-7280SE-64-FSerial Number JPE14443170EOS Version 4152F
Third Script - Using External Input
In the second script we have listed the switch IP addresses username and password in the script In this script wewill enter the IPs in a text file and enforce the script to prompt for username and password when the script is executed
Create a file named ldquoswitchesrdquo in the same directory as you are saving the Python scripts using any text editor of yourchoice And add the list of IP addresses of the switches Format the file as plain text (Format rarr Make Plain Text)
Open the IDLE and create a new python script named inventory_version_inputpy
Third script - Inventory - show version - Using External Input
import pyeapi
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Run this command and verify the value of the variable file from the python shell
33 Third Script - Using External Input 43
arista-python-web2py Documentation Release 001
Let us update the script to read the content of the file ldquoswitchesrdquo
Third script - Inventory - show version - Using External Input
import pyeapi
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
with open(file_switches) as readfilefor line in readfile
print line
Run the script and verify the result
gtgtgt ================================ RESTART ================================gtgtgt1722813241
1722813240
1722813245
We will store the IP addresses read from the file into a list
Third script - Inventory - show version - Using External Input
import pyeapi
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []
with open(file_switches) as readfilefor line in readfile
switchesappend(line)
Run this script
44 Chapter 3 Chapter 2 Script Framework for Use Cases
arista-python-web2py Documentation Release 001
gtgtgt ================================ RESTART ================================gtgtgt
You will not see any output since we are not sending any data to output
Type the variable name switches
gtgtgt switches[1722813241n 1722813240n 1722813245]
It appears that new line character ldquonrdquo is added in the list
gtgtgt type(switches)lttype listgt
gtgtgt switches[0]1722813241n
gtgtgt switches[1]1722813240n
You can strip the new line charactergtgtgt switches[0]strip()1722813241
Let us fix the script to strip the new line character
Third script - Inventory - show version - Using External Input
import pyeapi
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []
with open(file_switches) as readfilefor line in readfile
switchesappend(linestrip())
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtgtgtgt switches[1722813241 1722813240 1722813245]
Now let us get the username and password interactively instead of hard coding in the script
33 Third Script - Using External Input 45
arista-python-web2py Documentation Release 001
Third script - Inventory - show version - Using External Input
import pyeapi
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []
with open(file_switches) as readfilefor line in readfile
switchesappend(linestrip())
Input Username and Passwordmy_username = raw_input(Enter your username )my_password = raw_input(Enter your password )
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtEnter your username adminEnter your password admin
Obviously we donrsquot want to display the password on the screen when we type We will use the Python moduleldquogetpassrdquo to input password without displaying on the screen
Third script - Inventory - show version - Using External Input
import pyeapiimport getpass
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []
with open(file_switches) as readfile
46 Chapter 3 Chapter 2 Script Framework for Use Cases
arista-python-web2py Documentation Release 001
for line in readfileswitchesappend(linestrip())
Input Username and Password
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
When you run this script from IDLE (on Apple Mac) the password prompt will not be prompted in the IDLE shell Itwill be shown in the Apple Mac terminal where you have started the IDLE
You can also run your Python script from terminal
aneesmy-scripts anees$ pwdUsersaneesGoogle Drivemy-scripts
aneesmy-scripts anees$ lsinventory_versionpy switchestxtinventory_version_inputpy
aneesmy-scripts anees$ python inventory_version_inputpyEnter your username adminEnter your passwordaneesmy-scripts anees$
Now we have achieved our requirements of inputting switch IP addresses username and password from outside thepython script Let us complete the script with the inventory
Third script - Inventory - show version - Using External Input
33 Third Script - Using External Input 47
arista-python-web2py Documentation Release 001
import pyeapiimport getpass
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []
with open(file_switches) as readfilefor line in readfile
switchesappend(linestrip())
Input Username and Password
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
Collect Inventory using pyeapi
for switch in switches Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Execute the desired commandversion = nodeexecute([show version])
Print the desired Outputprintprint ()print (Switch IP s) (switch)print (Model Name s) (version[result][0][modelName])print (Serial Number s) (version[result][0][serialNumber])print (EOS Version s) (version[result][0][version])print ()print
Run the script
================================ RESTART ================================gtgtgtEnter your username admin
Switch IP 1722813241Model Name DCS-7050SX-128-FSerial Number JPE14080457EOS Version 4153F
48 Chapter 3 Chapter 2 Script Framework for Use Cases
arista-python-web2py Documentation Release 001
Switch IP 1722813240Model Name DCS-7050SX-128-FSerial Number JPE14080459EOS Version 4153F
Switch IP 1722813245Model Name DCS-7280SE-64-FSerial Number JPE14443170EOS Version 4153F
Fourth Script ndash Storing Result in a Dictionary
So far we output the data within the ldquofor looprdquo as and when we parse the required data using the print statement Inthis script we are going to save the output in a Python dictionary instead of printing within the ldquofor looprdquo At the endof the script we will print the dictionary using pprint
Before writing the script we need to come up with the structure of the dictionary Structure of the dictionary dependson what data we want to collect and report Our goal is to collect Model Name Serial Number and EOS version ofeach of the switches Hence the proposed dictionary structure is shown below
IP Address
Model NameSerial NumberEOS Version
Now the algorithm is going to look like this
1 Create a blank dictionary before ldquofor looprdquo inventory =
2 For each IP address create an entry (Key Value) with the IP address as the key and a blank dictionary as thevalue inventory[ldquo10101011rdquo] =
3 Add the entries for inventory[ldquo10101011rdquo][ldquoModel Namerdquo] inventory[ldquo10101011rdquo][ldquoSerial Numberrdquo] in-ventory[ldquo10101011rdquo][ldquoEOS Versionrdquo]
Launch Python interpreter from your terminal
anees~ anees$ pythonPython 2710 (default Aug 22 2015 203339)[GCC 421 Compatible Apple LLVM 700 (clang-7000591)] on darwinType help copyright credits or license for more information
gtgtgt inventory = gtgtgtgtgtgt type(inventory)lttype dictgtgtgtgt
34 Fourth Script ndash Storing Result in a Dictionary 49
arista-python-web2py Documentation Release 001
gtgtgt inventory[10101011] = gtgtgtgtgtgt inventory10101011 gtgtgtgtgtgt inventory[10101011][Model Name] = 7050SXgtgtgt inventory[10101011][Serial Number] = srx123456gtgtgt inventory[10101011][EOS Version] = 4153fgtgtgtgtgtgt inventory10101011 Serial Number srx123456 EOS Version 4153f Modelrarr˓Name 7050SXgtgtgtgtgtgt import pprintgtgtgtgtgtgt pprintpprint(inventory)10101011 EOS Version 4153f
Model Name 7050SXSerial Number srx123456
Let us go back and update our original inventory_version script Create a new python script with the name inven-tory_version_outputpy and save it in your folder
Fourth script - Inventory - show version - Store Result in a Dictionary
import pyeapiimport getpassimport pprint
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []
with open(file_switches) as readfilefor line in readfile
switchesappend(linestrip())
Input Username and Password
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
Collect Inventory using pyeapi
inventory =
for switch in switches
50 Chapter 3 Chapter 2 Script Framework for Use Cases
arista-python-web2py Documentation Release 001
inventory[switch] =
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Execute the desired commandversion = nodeexecute([show version])
Extract Desired data from show versionmodel_name = version[result][0][modelName]serial_number = version[result][0][serialNumber]EOS_version = version[result][0][version]
Update the inventory dictionary with the desired datainventory[switch][Model Name] = model_nameinventory[switch][Serial Number] = serial_numberinventory[switch][EOS Version] = EOS_version
pprintpprint(inventory)
Run this script and verify the result
gtgtgt ================================ RESTART ================================gtgtgtEnter your username admin1722813240 EOS Version u4153F
Model Name uDCS-7050SX-128-FSerial Number uJPE14080459
1722813241 EOS Version u4153FModel Name uDCS-7050SX-128-FSerial Number uJPE14080457
1722813245 EOS Version u4153FModel Name uDCS-7280SE-64-FSerial Number uJPE14443170
Fifth Script ndash Handling Exceptions
On the switchestxt file add an IP address of a switch that does not exist in the network or that does not have eAPIenabled For example the below list has the IP address 17228170143 which does not exist in the network
bull 1722813241
bull 17228170143
bull 1722813240
bull 1722813245
Create a new script inventory_version_exceptionpy in your folder Copy the script from inventory_version_outputpyand run the script
gtgtgt ================================ RESTART ================================gtgtgtEnter your username adminNo handlers could be found for logger pyeapieapilib
35 Fifth Script ndash Handling Exceptions 51
arista-python-web2py Documentation Release 001
Traceback (most recent call last)File UsersaneesGoogle Drivemy-scriptsinventory_version_exceptionpy line 46
rarr˓ in ltmodulegtversion = nodeexecute([show version])
File LibraryPython27site-packagespyeapieapilibpy line 464 in executeresponse = selfsend(request)
File LibraryPython27site-packagespyeapieapilibpy line 394 in sendraise ConnectionError(str(self) unable to connect to eAPI)
ConnectionError unable to connect to eAPI
Because of that one incorrect IP address the entire script fails This will be annoying in real world where you may bemanaging hundreds of devices and any one device that is not available at the moment you are running the script makethe entire script fail Software scriptming languages have a process called Exception Handling which should be usedto react to any errors or exceptions that impacts the normal flow of your script
Python has tryexcept keywords to handle exceptions so that you can run the scripts without exiting the script andreacts to the errors the way you wanted The below example is used to explain tryexcept keywords
tryinventory[switch] = Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Execute the desired commandversion = nodeexecute([show version])
Extract Desired data from show versionmodel_name = version[result][0][modelName]serial_number = version[result][0][serialNumber]EOS_version = version[result][0][version]
Update the inventory dictionary with the desired data
inventory[switch][Model Name] = model_nameinventory[switch][Serial Number] = serial_numberinventory[switch][EOS Version] = EOS_version
excepterrors[switch] = ConnectionError unable to connect to eAPI
The commands under the try section called as try clause and the commands under except section called asexcept clause If there is any exception occurs while executing try clause except clause will be executed and then thescript execution continues If there are no exceptions in the try clause except clause is skipped
We will update our script inventory_version_exceptionpy with tryexcept clause In this script we will create aseparate dictionary called ldquoerrorsrdquo in which we will store the IP addresses of the switch that fails the pyeapi call andthe corresponding error messages
Fifth script - Inventory - show version - Handling Exceptions
import pyeapiimport getpassimport pprint
52 Chapter 3 Chapter 2 Script Framework for Use Cases
arista-python-web2py Documentation Release 001
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []
with open(file_switches) as readfilefor line in readfile
switchesappend(linestrip())
Input Username and Password
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
Collect Inventory using pyeapi
Collect Inventory using pyeapi
inventory = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Execute the desired commandversion = nodeexecute([show version])
Extract Desired data from show versionmodel_name = version[result][0][modelName]serial_number = version[result][0][serialNumber]EOS_version = version[result][0][version]
Update the inventory dictionary with the desired datainventory[switch] = inventory[switch][Model Name] = model_nameinventory[switch][Serial Number] = serial_numberinventory[switch][EOS Version] = EOS_version
excepterrors[switch] = ConnectionError unable to connect to eAPI
pprintpprint(errors)pprintpprint(inventory)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgt
35 Fifth Script ndash Handling Exceptions 53
arista-python-web2py Documentation Release 001
Enter your username adminNo handlers could be found for logger pyeapieapilib17228170143 ConnectionError unable to connect to eAPI1722813240 EOS Version u4153F
Model Name uDCS-7050SX-128-FSerial Number uJPE14080459
1722813241 EOS Version u4153FModel Name uDCS-7050SX-128-FSerial Number uJPE14080457
1722813245 EOS Version u4153FModel Name uDCS-7280SE-64-FSerial Number uJPE14443170
Exception can happen due to variety of reasons For example it could be because of the switch is not reachable or theswitch is reachable but the EOS command entered in the script is incorrect In that case we are expecting the module(Pyeapi) that is used to facilitate the connection should differentiate the errors and allow the scriptmer to handle it inthe script Pyeapi module has few types of exceptions For more information about the exceptions supported by pyeapimodule refer the pyeapi module documentation Python Client for eAPI
You can also explore the modules from Python interpreter
gtgtgt dir(pyeapi)[__all__ __author__ __builtins__ __doc__ __file__ __name__ __rarr˓package__ __path__ __version__ client config_for connect connect_rarr˓to eapilib load_config utils]
gtgtgt dir(pyeapieapilib)[CommandError ConnectionError DEFAULT_HTTPS_PORT DEFAULT_HTTP_LOCAL_PORTrarr˓DEFAULT_HTTP_PATH DEFAULT_HTTP_PORT DEFAULT_UNIX_SOCKET EapiConnectionrarr˓EapiError HTTPConnection HTTPSConnection HttpConnectionrarr˓HttpEapiConnection HttpLocalEapiConnection HttpsConnectionrarr˓HttpsEapiConnection SocketConnection SocketEapiConnection _LOGGER __rarr˓builtins__ __doc__ __file__ __name__ __package__ base64 debugrarr˓https_connection_factory json logging make_iterable socket sslrarr˓sys]
We can update our script inventory_version_exceptionpy to differentiate the type of pyeapi exceptions
Fifth script - Inventory - show version - Handling Exceptions
import pyeapiimport getpassimport pprint
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
54 Chapter 3 Chapter 2 Script Framework for Use Cases
arista-python-web2py Documentation Release 001
switches = []
with open(file_switches) as readfilefor line in readfile
switchesappend(linestrip())
Input Username and Password
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
Collect Inventory using pyeapi
inventory = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Execute the desired commandversion = nodeexecute([show version])
Extract Desired data from show versionmodel_name = version[result][0][modelName]serial_number = version[result][0][serialNumber]EOS_version = version[result][0][version]
Update the inventory dictionary with the desired datainventory[switch] = inventory[switch][Model Name] = model_nameinventory[switch][Serial Number] = serial_numberinventory[switch][EOS Version] = EOS_version
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
pprintpprint(errors)pprintpprint(inventory)
Verify the error messages
gtgtgt ================================ RESTART ================================gtgtgtEnter your username adminNo handlers could be found for logger pyeapieapilib17228170143 ConnectionError unable to connect to eAPI1722813240 EOS Version u4153F
Model Name uDCS-7050SX-128-FSerial Number uJPE14080459
1722813241 EOS Version u4153FModel Name uDCS-7050SX-128-FSerial Number uJPE14080457
35 Fifth Script ndash Handling Exceptions 55
arista-python-web2py Documentation Release 001
1722813245 EOS Version u4153FModel Name uDCS-7280SE-64-FSerial Number uJPE14443170
Change the command syntax ldquoshow versionrdquo to ldquoshow verrdquo and run the command
gtgtgt ================================ RESTART ================================gtgtgtEnter your username adminNo handlers could be found for logger pyeapieapilib1722813240 CommandError Check your EOS command syntax1722813241 CommandError Check your EOS command syntax1722813245 CommandError Check your EOS command syntax17228170143 ConnectionError unable to connect to eAPI
Summary ndash Script Framework
We have learned various Python concepts step by step using the inventory use case and finally we got a structure forour network use case Python script if you have observed closely all the five scripts The structure of code is shownbelow
Section 1 Import Modules
import pyeapiimport getpassimport pprint
Section 2 Read the list of switch IP addresses from a file and store it in a Python list
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
switches = []
with open(file_switches) as readfilefor line in readfile
switchesappend(linestrip())
Section 3 Input Username and Password that have access to the switches
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
Section 4 Main Algorithm Specific to your use case
For every switch in the Python list
1 Connect to the switch using Aristarsquos pyeapi module
2 Execute the commands needed for your logic
3 Core Logic
4 Store the result into a dictionary
56 Chapter 3 Chapter 2 Script Framework for Use Cases
arista-python-web2py Documentation Release 001
inventory = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Execute the desired commandversion = nodeexecute([show version])
Extract Desired data from show versionmodel_name = version[result][0][modelName]serial_number = version[result][0][serialNumber]EOS_version = version[result][0][version]
Update the inventory dictionary with the desired datainventory[switch] = inventory[switch][Model Name] = model_nameinventory[switch][Serial Number] = serial_numberinventory[switch][EOS Version] = EOS_version
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5 Print the dictionaries
pprintpprint(errors)pprintpprint(inventory)
We are going to use this five sections framework for all the use cases in this document In all the use cases we reusethe commands from sections 1 2 3 4A and 5 Only sections that changes based on the requirements of use cases are4B 4C and 4D
36 Summary ndash Script Framework 57
arista-python-web2py Documentation Release 001
58 Chapter 3 Chapter 2 Script Framework for Use Cases
CHAPTER 4
Chapter 3 Troubleshooting Use Cases
bull Monitor Data Plane Drops
ndash Arista EOS Show Commands
ndash Develop Script
ndash Final Script
bull Monitor Control Plane Drops
ndash Arista EOS Show Commands
ndash Develop Script
ndash Final Script
This chapter shows how to build Python scripts for a couple of network troubleshooting use cases There are no newPython concepts introduced in this chapter but it gives a good practice to use the framework and Python concepts youlearned so far in this book For all the use cases first we are going to write a high level troubleshooting steps then wewill start writing the script step by step based on the troubleshooting steps
Monitor Data Plane Drops
The goal of this script is to discover if there are any packet drops in any of the switches in the network First we willwrite down the troubleshooting steps and then we will build the script
Arista EOS Show Commands
Our requirement is to list out the name of the switches interface numbers and the corresponding drop counters Youcan collect all the information using ldquoshow interfacesrdquo command In this example we are going to use ldquoshow interfaces
59
arista-python-web2py Documentation Release 001
counters discardsrdquo to find the interfaces that see drops and then collect the ldquoshow interfacerdquo for that particular interfaceand get the detailed drop counters
Step 1 Identify if there are any drops in the switch and obtain the interface numbers
Arista-switch show interfaces counters discards | json
inDiscardsTotal 0interfaces
Ethernet8 outDiscards 0inDiscards 0
Ethernet9
outDiscards 0inDiscards 0
Ethernet2
outDiscards 0inDiscards 0
Ethernet3
outDiscards 0inDiscards 0
Ethernet1
outDiscards 0inDiscards 0
Ethernet21 outDiscards 45941inDiscards 0
Step 2 If any of the interfaces has non-zero counter in inDiscards or outDiscards collect the show interface and printthe detailed output or input error counters
Arista-switch show interfaces ethernet 21 | json
interfaces Ethernet21
lastStatusChangeTimestamp 14507349228865843name Ethernet21interfaceStatus connectedautoNegotiate successburnedInAddress 001c73574f5eloopbackMode loopbackNoneinterfaceStatistics
inBitsRate 07396139208713536inPktsRate 00007222792196009312outBitsRate 5494475135300596updateInterval 50outPktsRate 09075684364502475
mtu 9214hardware ethernetduplex duplexFullbandwidth 1000000000forwardingModel dataLink
60 Chapter 4 Chapter 3 Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
lineProtocolStatus upinterfaceCounters
outBroadcastPkts 11025linkStatusChanges 5totalOutErrors 0inMulticastPkts 754counterRefreshTime 1450757484079082inBroadcastPkts 0outputErrorsDetail
deferredTransmissions 0txPause 0collisions 0lateCollisions 0
inOctets 96512outDiscards 45941outOctets 78324214888inUcastPkts 0inputErrorsDetail
runtFrames 0rxPause 0fcsErrors 0alignmentErrors 0giantFrames 0symbolErrors 0
outUcastPkts 152941271outMulticastPkts 1512totalInErrors 0inDiscards 0
interfaceMembership Member of Port-Channel10interfaceAddress []physicalAddress 001c73574f5edescription F5-2
Develop Script
Open the IDLE and create a new script and save as interface_dropspy in your folder Copy the code from section 12 3 from our framework and paste it in this new script
Discover if there is any packet drops in the network
Section 1
import pyeapiimport getpassimport pprint
Section 2
41 Monitor Data Plane Drops 61
arista-python-web2py Documentation Release 001
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []with open(file_switches) as readfile
for line in readfileswitchesappend(linestrip())
Section 3
Input Username and Password
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
Step 1 Identify interfaces having packet drops
Start section 4 with the usual for loop and pyeapi command Collect ldquoshow interfaces counters discardsrdquo output fromthe switches Later in this script we will store the result in a dictionary we will name as interface_drops
Section 4
interface_drops = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Execute the desired commandinterface_counters = nodeexecute([show interfaces counters discards])
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
pprintpprint(errors)
Save and run the script We are going to explore the dictionary to find the exact key to see the the packet drops
================================ RESTART ================================gtgtgtEnter your username admingtgtgtgtgtgt pprintpprint(interface_counters)uid u4408635024
62 Chapter 4 Chapter 3 Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
ujsonrpc u20uresult [uinDiscardsTotal 0
uinterfaces uEthernet11 uinDiscards 0uoutDiscards 0
uEthernet101 uinDiscards 0uoutDiscards 0
uEthernet102 uinDiscards 0uoutDiscards 0
uEthernet103 uinDiscards 0uoutDiscards 0
gtgtgt pprintpprint(interface_counters[result])[uinDiscardsTotal 0
uinterfaces uEthernet11 uinDiscards 0 uoutDiscards 0uEthernet101 uinDiscards 0 uoutDiscards 0uEthernet102 uinDiscards 0 uoutDiscards 0uEthernet103 uinDiscards 0 uoutDiscards 0uEthernet104 uinDiscards 0 uoutDiscards 0uEthernet111
gtgtgt pprintpprint(interface_counters[result][0])uinDiscardsTotal 0uinterfaces uEthernet11 uinDiscards 0 uoutDiscards 0
uEthernet101 uinDiscards 0 uoutDiscards 0uEthernet102 uinDiscards 0 uoutDiscards 0uEthernet103 uinDiscards 0 uoutDiscards 0uEthernet104 uinDiscards
gtgtgt pprintpprint(interface_counters[result][0][interfaces])uEthernet11 uinDiscards 0 uoutDiscards 0uEthernet101 uinDiscards 0 uoutDiscards 0uEthernet102 uinDiscards 0 uoutDiscards 0uEthernet103 uinDiscards 0 uoutDiscards 0uEthernet104 uinDiscards 0 uoutDiscards 0uEthernet111 uinDiscards 0 uoutDiscards 0uEthernet112 uinDiscards 0 uoutDiscards 0uEthernet113 uinDiscards 0 uoutDiscards 0uEthernet114 uinDiscards 0 uoutDiscards 0uEthernet121 uinDiscards 0
gtgtgt interface_counters_clean = interface_counters[result][0][interfaces]gtgtgtgtgtgt pprintpprint(interface_counters_cleankeys())[uEthernet43uEthernet554uEthernet154uEthernet263uEthernet152uEthernet541uEthernet484uEthernet584uEthernet481uEthernet482uEthernet483
41 Monitor Data Plane Drops 63
arista-python-web2py Documentation Release 001
uEthernet583uEthernet363uEthernet362
gtgtgt interface_counters_clean[Ethernet43]uinDiscards 0 uoutDiscards 0gtgtgt interface_counters_clean[Ethernet43][inDiscards]0gtgtgt interface_counters_clean[Ethernet43][outDiscards]0
For every result from a pyeapi call our interesting data is under interface_counters [ldquoresultsrdquo] [0] [ldquointerfacesrdquo] keyNow we know the counters to see the packet drops which is interface_counters_clean [ltinterfacegt] [ldquoinDiscardsrdquo]
Step 2 Collect the InputOutput Drops Statistics
We are going to iterate through the interfaces within each switch and look for non zero counters If we see a non zerocounter we will initiate a pyeapi call to collect the ldquoshow interfacerdquo for that specific interface
Section 4
interface_drops = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Execute the desired commandinterface_counters = nodeexecute([show interfaces counters discards])interface_counters_clean = interface_counters[result][0][interfaces]
Parse through each interface and see if there any non zero inDiscards or
rarr˓outDiscardsIf any interface has non zero counters collect show interface ouput of
rarr˓that interfacefor interface in interface_counters_cleankeys()
if interface_counters_clean[interface][inDiscards] or interface_rarr˓counters_clean[interface][outDiscards] = 0
show_interface = nodeexecute([show interfaces + str(interface)])
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
pprintpprint(errors)
Save and run the script Now we are going to explore within the ldquoshow interfacerdquo output what are the specific countersto collect and report
64 Chapter 4 Chapter 3 Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
gtgtgt show_interface[result][0][interfaces][Ethernet21][interfaceCounters][rarr˓inputErrorsDetail]
uruntFrames 0 ufcsErrors 0 ualignmentErrors 0 urxPause 0 urarr˓symbolErrors 0 ugiantFrames 0
gtgtgt show_interface[result][0][interfaces][Ethernet21][interfaceCounters][rarr˓outputErrorsDetail]
ulateCollisions 0 udeferredTransmissions 0 utxPause 0 ucollisions 0
Now we know the key for the input and output error details What we need to know is to whether to collect the inputor output error details You can use if statements to collect input or output error details depends on whether you seeinput or output discards
Section 4
interface_drops = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
41 Monitor Data Plane Drops 65
arista-python-web2py Documentation Release 001
Execute the desired commandinterface_counters = nodeexecute([show interfaces counters discards])interface_counters_clean = interface_counters[result][0][interfaces]
Parse through each interface and see if there any non zero inDiscards or
rarr˓outDiscardsIf any interface has non zero counters collect show interface ouput of
rarr˓that interfacefor interface in interface_counters_cleankeys()
if interface_counters_clean[interface][inDiscards] or interface_rarr˓counters_clean[interface][outDiscards] = 0
show_interface = nodeexecute([show interfaces + str(interface)])show_interface_clean = show_interface[result][0][interfaces
rarr˓][interface][interfaceCounters]if interface_counters_clean[interface][inDiscards] = 0
print show_interface_clean[inputErrorsDetail]if interface_counters_clean[interface][outDiscards] = 0
print show_interface_clean[outputErrorsDetail]
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
pprintpprint(errors)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtEnter your username admin
Traceback (most recent call last)File UsersaneesGoogle Drivemy-scriptsinterface_dropspy line 60 in ltmodulegtprint show_interface_clean[outputErrorsDetail]
KeyError outputErrorsDetail
We are seeing an error message in the above output It says that there is no key ouputErrorsDetail in the showinterface ltspecific interfacegt If you pprint the show interface ltspecific interfacegt output you can see that thereare no outputErrorsDetail key under interfaceCounters section This is because the output shown is for port channelinterface This specific counter is applicable only for physical interface So we are going to add a check so that if theinterface is port channel we are not going to look for any counters
66 Chapter 4 Chapter 3 Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
We are going to do the interface check using ldquoif lsquoPort-Channelrsquo not in interfacerdquo logic It would be better idea to usethis logic ldquoif lsquoEthernetrsquo in interfacerdquo instead of ldquo lsquoPort-Channelrsquo not inrdquo Because show interfaces will also containsSVIs We need to look at only the Ethernet interfaces However for learning perspective we are going to use ldquoiflsquoPort-Channelrsquo not in interfacerdquo
Section 4
interface_drops = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Execute the desired commandinterface_counters = nodeexecute([show interfaces counters discards])interface_counters_clean = interface_counters[result][0][interfaces]
Parse through each interface and see if there any non zero inDiscards or
rarr˓outDiscardsIf any interface has non zero counters collect show interface output of
rarr˓that interfacefor interface in interface_counters_cleankeys()
if Port-Channel not in interfaceif interface_counters_clean[interface][inDiscards] or interface_
rarr˓counters_clean[interface][outDiscards] = 0show_interface = nodeexecute([show interfaces +
rarr˓str(interface)])
41 Monitor Data Plane Drops 67
arista-python-web2py Documentation Release 001
show_interface_clean = show_interface[result][0][interfacesrarr˓][interface][interfaceCounters]
if interface_counters_clean[interface][inDiscards] = 0print show_interface_clean[inputErrorsDetail]
if interface_counters_clean[interface][outDiscards] = 0print show_interface_clean[outputErrorsDetail]
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
pprintpprint(errors)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtEnter your username admin
ulateCollisions 0 udeferredTransmissions 0 utxPause 0 ucollisions 0
Step 3 Saving the Result in a Dictionary
Let us store the result in a nice format in a dictionary
interface_drops =
Switch_host_name Interface_number
ldquoInterface statusrdquo ltvaluegtldquoLine Protocol Statusrdquo ltvaluegtldquoinDiscardsrdquo
ldquoTotal Discardsrdquo ltvaluegtInputErrorsDetail
ldquooutDiscardsrdquo
ldquoTotal DiscardsrdquoldquooutputErrorsDetailrdquo
First you need to initiate the dictionary when you start the section 4 in the script
interface_drops = for switch in switches
For every switch create a key value pair in the interface_drops dictionary Here the value is another dictionary Sinceit has to be created for each switch we create this line inside the ldquofor looprdquo of the switches You need to get thehostname of the switch using the command ldquoshow hostnamerdquo
interface_drops = for switch in switches
68 Chapter 4 Chapter 3 Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
try lines skippedhost_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])interface_drops[host_name_clean] =
For each switch we need to create a key value pair for the interface we are seeing packet drops This statement shouldbe inside the ldquofor looprdquo of the interfaces of each switch Since we need to create the key value pair only if you seeany packet drops this line will be inside the if statement
interface_drops = for switch in switches
try lines skippedinterface_drops[host_name_clean] = for interface in interface_counters_cleankeys()
if interface_counters_clean[interface][inDiscards] or interface_rarr˓counters_clean[interface][outDiscards] = 0
interface_drops[host_name_clean][interface] =
If there is any input discards we will record the total input discards and input error statistics
if interface_counters_clean[interface][inDiscards] = 0interface_drops[host_name_clean][interface][inDiscards] = interface_drops[host_name_clean][interface][inDiscards][Total Discards] =
rarr˓interface_counters_clean[interface][inDiscards]interface_drops[host_name_clean][interface][inDiscards][Input Errors] = show_
rarr˓interface_clean[inputErrorsDetail]
Similarly if there is any output discards then we will record the total output discards and output error statistics
if interface_counters_clean[interface][outDiscards] = 0interface_drops[host_name_clean][interface][outDiscards] = interface_drops[host_name_clean][interface][outDiscards][Total Discards] =
rarr˓interface_counters_clean[interface][outDiscards]interface_drops[host_name_clean][interface][outDiscards][Output Errors] =
rarr˓show_interface_clean[outputErrorsDetail]
Now let us look at the section 4 of the script we have developed so far
Section 4
interface_drops = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Execute the desired commandinterface_counters = nodeexecute([show interfaces counters discards])interface_counters_clean = interface_counters[result][0][interfaces]
Collect hostname for documenting results under the host namehost_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])
41 Monitor Data Plane Drops 69
arista-python-web2py Documentation Release 001
interface_drops[host_name_clean] =
Parse through each interface and see if there any non zero inDiscards or
rarr˓outDiscardsIf any interface has non zero counters collect show interface output of
rarr˓that interface
for interface in interface_counters_cleankeys() Skip if the interface is port channelif Port-Channel not in interface
if interface_counters_clean[interface][inDiscards] or interface_rarr˓counters_clean[interface][outDiscards] = 0
Create an entry for the interface under the host nameinterface_drops[host_name_clean][interface] =
Collect the interface statistics from the switchshow_interface = nodeexecute([show interfaces +
rarr˓str(interface)])show_interface_clean = show_interface[result][0][interfaces
rarr˓][interface][interfaceCounters]
Collect interface and line protocol status just forrarr˓documentation purpose
interface_drops[host_name_clean][interface][Interface Status] =rarr˓show_interface[result][0][interfaces][interface][interfaceStatus]
interface_drops[host_name_clean][interface][Line Protocol Statusrarr˓] = show_interface[result][0][interfaces][interface][lineProtocolStatus]
Collect detailed input or output drop counters if there arerarr˓input or output drops
if interface_counters_clean[interface][inDiscards] = 0interface_drops[host_name_clean][interface][inDiscards] = interface_drops[host_name_clean][interface][inDiscards][
rarr˓Total Discards] = interface_counters_clean[interface][inDiscards]interface_drops[host_name_clean][interface][inDiscards][
rarr˓Input Errors] = show_interface_clean[inputErrorsDetail]if interface_counters_clean[interface][outDiscards] = 0
interface_drops[host_name_clean][interface][outDiscards] =rarr˓
interface_drops[host_name_clean][interface][outDiscards][rarr˓Total Discards] = interface_counters_clean[interface][outDiscards]
interface_drops[host_name_clean][interface][outDiscards][rarr˓Output Errors] = show_interface_clean[outputErrorsDetail]
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
pprintpprint(errors)pprintpprint(interface_drops)
Save and run the script
70 Chapter 4 Chapter 3 Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
gtgtgt ================================ RESTART ================================gtgtgtEnter your username admin22sw2 uEthernet21 Interface Status uconnected
Line Protocol Status uupoutDiscards Output Errors ucollisions 0
udeferredTransmissions 0ulateCollisions 0utxPause 0
Total Discards 4594122sw35 22sw37 22sw4
As you see we are seeing some of the empty dictionaries printed in the output We can add additional checks in thecode to print non empty dictionaries For example the first empty dictionary in the output is printed as part ofpprintpprint(errors) We want to print only the non empty dictionaries
The following is one of the methods to check whether a dictionary is empty or not
anees~ anees$ pythonPython 2710 (default Aug 22 2015 203339)[GCC 421 Compatible Apple LLVM 700 (clang-7000591)] on darwinType help copyright credits or license for more informationgtgtgt Create an empty dictionarygtgtgt errors = gtgtgtgtgtgt not errorsTruegtgtgtgtgtgt bool(errors)Falsegtgtgtgtgtgt if not errors print Empty DictionaryEmpty Dictionarygtgtgtgtgtgt Create a non empty dictionarygtgtgt errors = testgtgtgtgtgtgt not errorsFalsegtgtgtgtgtgt bool(errors)Truegtgtgt if bool(errors) print NOT EmptyNOT Empty
With these additional checks we will print errors only if the errors dictionary is non empty and we will not save theswitch entry if there are no drops found in that switch
Section 4
interface_drops =
41 Monitor Data Plane Drops 71
arista-python-web2py Documentation Release 001
errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Execute the desired commandinterface_counters = nodeexecute([show interfaces counters discards])interface_counters_clean = interface_counters[result][0][interfaces]
Collect hostname for documenting results under the host namehost_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])interface_drops[host_name_clean] =
Parse through each interface and see if there any non zero inDiscards or
rarr˓outDiscardsIf any interface has non zero counters collect show interface output of
rarr˓that interface
for interface in interface_counters_cleankeys() Skip if the interface is port channelif Port-Channel not in interface
if interface_counters_clean[interface][inDiscards] or interface_rarr˓counters_clean[interface][outDiscards] = 0
Create an entry for the interface under the host nameinterface_drops[host_name_clean][interface] =
Collect the interface statistics from the switchshow_interface = nodeexecute([show interfaces +
rarr˓str(interface)])show_interface_clean = show_interface[result][0][interfaces
rarr˓][interface][interfaceCounters]
Collect interface and line protocol status just forrarr˓documentation purpose
interface_drops[host_name_clean][interface][Interface Status] =rarr˓show_interface[result][0][interfaces][interface][interfaceStatus]
interface_drops[host_name_clean][interface][Line Protocol Statusrarr˓] = show_interface[result][0][interfaces][interface][lineProtocolStatus]
Collect detailed input or output drop counters if there arerarr˓input or output drops
if interface_counters_clean[interface][inDiscards] = 0interface_drops[host_name_clean][interface][inDiscards] = interface_drops[host_name_clean][interface][inDiscards][
rarr˓Total Discards] = interface_counters_clean[interface][inDiscards]interface_drops[host_name_clean][interface][inDiscards][
rarr˓Input Errors] = show_interface_clean[inputErrorsDetail]if interface_counters_clean[interface][outDiscards] = 0
interface_drops[host_name_clean][interface][outDiscards] =rarr˓
interface_drops[host_name_clean][interface][outDiscards][rarr˓Total Discards] = interface_counters_clean[interface][outDiscards]
72 Chapter 4 Chapter 3 Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
interface_drops[host_name_clean][interface][outDiscards][rarr˓Output Errors] = show_interface_clean[outputErrorsDetail]
if not interface_drops[host_name_clean]del interface_drops[host_name_clean]
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
if bool(errors)pprintpprint(errors)
if bool(interface_drops)pprintpprint(interface_drops)
Save and run the script You will no longer see the empty dictionaries
================================ RESTART ================================gtgtgtEnter your username admin22sw2 uEthernet21 Interface Status uconnected
Line Protocol Status uupoutDiscards Output Errors ucollisions 0
udeferredTransmissions 0ulateCollisions 0utxPause 0
Total Discards 45941
Step 4 Tracking interfaces with incrementing packet drops
If you want to find the interface that has incrementing packet drops we need to add additional logic to the script Thelogic which we are going to use here is that first we will discover all the interfaces having non zero packet drops byusing the script we developed Then we will wait for 1 minute and again collect the statistics for the same interfaceswe have noticed packet drops and compare the result If there is a difference we will save the new statistics in thedictionary
Section 1
import time
Section 4
for switch in switchestry
for interface in interface_counters_cleankeys()if Port-Channel not in interface
if interface_counters_clean[interface][inDiscards] or interface_rarr˓counters_clean[interface][outDiscards] = 0
timesleep(60)interface_counters_new = nodeexecute([show interfaces +
rarr˓str(interface) + counters discards])interface_counters_new_clean = interface_counters_new[result
rarr˓][0][interfaces]
41 Monitor Data Plane Drops 73
arista-python-web2py Documentation Release 001
input_discards_difference = interface_counters_new_rarr˓clean[interface][inDiscards] - interface_counters_clean[interface][inDiscards]
output_discards_difference = interface_counters_new_rarr˓clean[interface][outDiscards] - interface_counters_clean[interface][outDiscards]
Now we will add the if statement to verify if there is any difference in the packet drops counters we will store theresult in the dictionary
if input_discards_difference or output_discards_difference = 0interface_drops[host_name_clean][interface] = show_interface = nodeexecute([show interfaces + str(interface)])show_interface_clean = show_interface[result][0][interfaces][interface][
rarr˓interfaceCounters]interface_drops[host_name_clean][interface][Interface Status] = show_interface[
rarr˓result][0][interfaces][interface][interfaceStatus]interface_drops[host_name_clean][interface][Line Protocol Status] = show_
rarr˓interface[result][0][interfaces][interface][lineProtocolStatus]
if interface_counters_new_clean[interface][inDiscards] = 0interface_drops[host_name_clean][interface][inDiscards] = interface_drops[host_name_clean][interface][inDiscards][Total Discards] =
rarr˓interface_counters_new_clean[interface][inDiscards]interface_drops[host_name_clean][interface][inDiscards][Input Errors] =
rarr˓show_interface_clean[inputErrorsDetail]
if interface_counters_new_clean[interface][outDiscards] = 0interface_drops[host_name_clean][interface][outDiscards] = interface_drops[host_name_clean][interface][outDiscards][Total Discards]
rarr˓= interface_counters_new_clean[interface][outDiscards]interface_drops[host_name_clean][interface][outDiscards][Output Errors] =
rarr˓show_interface_clean[outputErrorsDetail]
Final Script
Here is the final script that shows if there are any continuous packet drops in the network
Discover if there is any packet drops in the network
Section 1
import pyeapiimport getpassimport pprintimport time
Section 2
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
74 Chapter 4 Chapter 3 Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
switches = []with open(file_switches) as readfile
for line in readfileswitchesappend(linestrip())
Section 3
Input Username and Password
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
Section 4
interface_drops = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Collect the interface drops statistics from the switchinterface_counters = nodeexecute([show interfaces counters discards])interface_counters_clean = interface_counters[result][0][interfaces]
Collect hostname for documenting results under the host namehost_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])interface_drops[host_name_clean] =
Parse through each interface and see if there any non zero inDiscards or
rarr˓outDiscardsIf any interface has non zero counters collect show interface output of
rarr˓that interface
for interface in interface_counters_cleankeys() Skip if the interface is port channelif Port-Channel not in interface
if interface_counters_clean[interface][inDiscards] or interface_rarr˓counters_clean[interface][outDiscards] = 0
Collect interface drops statistics for this specific interfacerarr˓after 1 minute
timesleep(60)interface_counters_new = nodeexecute([show interfaces +
rarr˓str(interface) + counters discards])interface_counters_new_clean = interface_counters_new[result
rarr˓][0][interfaces]
Identify the difference in packet dropsinput_discards_difference = interface_counters_new_
rarr˓clean[interface][inDiscards] - interface_counters_clean[interface][inDiscards]output_discards_difference = interface_counters_new_
rarr˓clean[interface][outDiscards] - interface_counters_clean[interface][outDiscards]
41 Monitor Data Plane Drops 75
arista-python-web2py Documentation Release 001
If the packet drops counter increments collect and storerarr˓detailed statistics in the dictionary
if input_discards_difference or output_discards_difference = 0interface_drops[host_name_clean][interface] = show_interface = nodeexecute([show interfaces +
rarr˓str(interface)])show_interface_clean = show_interface[result][0][interfaces
rarr˓][interface][interfaceCounters]
Collect interface and line protocol status just forrarr˓documentation purpose
interface_drops[host_name_clean][interface][Interface Statusrarr˓] = show_interface[result][0][interfaces][interface][interfaceStatus]
interface_drops[host_name_clean][interface][Line Protocolrarr˓Status] = show_interface[result][0][interfaces][interface][lineProtocolStatusrarr˓]
Collect detailed input or output drop countersif interface_counters_new_clean[interface][inDiscards] = 0
interface_drops[host_name_clean][interface][inDiscards]rarr˓=
interface_drops[host_name_clean][interface][inDiscards][rarr˓Total Discards] = interface_counters_new_clean[interface][inDiscards]
interface_drops[host_name_clean][interface][inDiscards][rarr˓Input Errors] = show_interface_clean[inputErrorsDetail]
if interface_counters_new_clean[interface][outDiscards] =rarr˓0
interface_drops[host_name_clean][interface][outDiscardsrarr˓] =
interface_drops[host_name_clean][interface][outDiscardsrarr˓][Total Discards] = interface_counters_new_clean[interface][outDiscards]
interface_drops[host_name_clean][interface][outDiscardsrarr˓][Output Errors] = show_interface_clean[outputErrorsDetail]
Delete the dictionary entry for the switch if the switch does not have anyrarr˓incremental packet drops
if not interface_drops[host_name_clean]del interface_drops[host_name_clean]
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
print the result only if the dictionary is non emptyif bool(errors)
pprintpprint(errors)
if bool(interface_drops)pprintpprint(interface_drops)
Change the troubleshooting steps and modify the script as per your troubleshooting approach In this use case wecollect interface drop statistics in 1 minute interval for each interface from each switch that sees packet drops You
76 Chapter 4 Chapter 3 Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
can modify this logic in different ways Instead of waiting for 1 minute for each interface of each switches you cancollect interface drop statistics in 1 minute interval for each switch Or You can collect interface drop statistics in 1minute interval for all the switches at once and compare the results
Monitor Control Plane Drops
The goal of this script is to discover if there are any control plane drops in any of the switches in the network First wewill write down the troubleshooting steps and then we will build the script
Arista EOS Show Commands
Step 1 Identify if there are any control plane drops in the switch
Arista-switchshow policy-map interface control-plane copp-system-policy | json
policyMaps copp-system-policy
mapType mapControlPlaneclassMaps
copp-system-ptp shape
unit ppsrate 2500
classPrio 13mapType mapControlPlanematchCondition matchConditionAnyintfPacketCounters
outPackets 0dropPackets 0
bandwidth
unit ppsrate 500
match name copp-system-ptp
copp-system-arp
shape unit ppsrate 10000
classPrio 19mapType mapControlPlanematchCondition matchConditionAnyintfPacketCounters
outPackets 192696dropPackets 0
bandwidth
unit ppsrate 1000
match
42 Monitor Control Plane Drops 77
arista-python-web2py Documentation Release 001
name copp-system-arp
Step 2 If we find any of the copp classes have drops we will save the result in a directory
Copp_drops = ldquoswitch_host_namerdquo ldquocopp-class-map-namerdquo
ldquodroppacketsrdquo ltno of dropped packetsgt
Step 3 We will add the logic to show the control plane drops only if the counters are incrementing
Develop Script
Step 1 Initial script and explore output counters
Open the IDLE and create a new script and save as copp_dropspy in your folder Copy the code from section 1 2 3from our framework and paste it in this new script
Discover if there is any control plane drops in the network
Section 1
import pyeapiimport getpassimport pprint
Section 2
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []with open(file_switches) as readfile
for line in readfileswitchesappend(linestrip())
Section 3
Input Username and Password
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
Start section 4 with the usual ldquofor looprdquo and pyeapi command Collect ldquoshow policy-map interface control-plane copp-system-policyrdquo output from the switches Then from IDLE shell we will explore how to pull the required counters
78 Chapter 4 Chapter 3 Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
Section 4
copp_drops = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Execute the desired commandcopp_counters = nodeexecute([show policy-map interface control-plane copp-
rarr˓system-policy])
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
if bool(errors)pprintpprint(errors)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtEnter your username admingtgtgt pprintpprint(copp_counters)uid u4585156240ujsonrpc u20uresult [upolicyMaps ucopp-system-policy uclassMaps ucopp-system-rarr˓OspfIsis ubandwidth urate 5000
gtgtgt pprintpprint(copp_counters[result][0][policyMaps][copp-system-policy][rarr˓classMaps])ucopp-system-OspfIsis ubandwidth urate 5000 uunit upps
uclassPrio 11uintfPacketCounters udropPackets 0
uoutPackets 0umapType umapControlPlaneumatch umatchCondition umatchConditionAnyuname ucopp-system-OspfIsisushape urate 10000 uunit upps
ucopp-system-acllog ubandwidth urate 1000 uunit uppsuclassPrio 30uintfPacketCounters udropPackets 0
uoutPackets 0umapType umapControlPlaneumatch umatchCondition umatchConditionAnyuname ucopp-system-acllogushape urate 10000 uunit upps
42 Monitor Control Plane Drops 79
arista-python-web2py Documentation Release 001
gtgtgt copp_counters_clean = copp_counters[result][0][policyMaps][copp-system-policyrarr˓][classMaps]gtgtgtgtgtgt copp_counters_clean[copp-system-acllog][intfPacketCounters][dropPackets]0gtgtgtgtgtgt copp_counters_cleankeys()[ucopp-system-selfip ucopp-system-tc6to7 ucopp-system-l3slowpath ucopp-rarr˓system-arp ucopp-system-lacp ucopp-system-arpresolver ucopp-system-tc3to5rarr˓ ucopp-system-default ucopp-system-bpdu ucopp-system-pim ucopp-system-rarr˓vxlan-vtep-learn ucopp-system-urm ucopp-system-bfd ucopp-system-vxlan-rarr˓encapsulation ucopp-system-ipmcrsvd ucopp-system-mlag ucopp-system-igmp urarr˓copp-system-lldp ucopp-system-ptp ucopp-system-vrrp ucopp-system-l3ttl1rarr˓ucopp-system-selfip-tc6to7 ucopp-system-acllog ucopp-system-cvx ucopp-rarr˓system-l3destmiss ucopp-system-OspfIsis ucopp-system-cvx-heartbeat ucopp-rarr˓system-sflow ucopp-system-ipmcmiss ucopp-system-glean ucopp-system-bgp]
Step 2 Add logic to discover Non Zero Counters and print the result
Since we know what counters to look we will use if statement to verify for non zero counters If we find a non zerocounter we will print the host name copp policy name and drop packets counter
Section 4
copp_drops = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Execute the desired commandcopp_counters = nodeexecute([show policy-map interface control-plane copp-
rarr˓system-policy])copp_counters_clean = copp_counters[result][0][policyMaps][copp-system-
rarr˓policy][classMaps]
parse through each copp system class map and discover non drop countersfor each_copp_class in copp_counters_cleankeys()
if copp_counters_clean[each_copp_class][intfPacketCounters][dropPacketsrarr˓] = 0
print host_name_clean each_copp_class copp_counters_clean[each_copp_rarr˓class][intfPacketCounters][dropPackets]
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
if bool(errors)pprintpprint(errors)
80 Chapter 4 Chapter 3 Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtEnter your username admin22sw2 copp-system-glean 103342722sw4 copp-system-glean 222836922sw37 copp-system-default 122593122sw37 copp-system-glean 10606
Step 3 Store the result in a dictionary
Section 4
copp_drops = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
Find the host name of the switch for reportinghost_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])copp_drops[host_name_clean] =
Execute the desired commandcopp_counters = nodeexecute([show policy-map interface control-plane copp-
rarr˓system-policy])copp_counters_clean = copp_counters[result][0][policyMaps][copp-system-
rarr˓policy][classMaps]
parse through each copp system class map and discover non drop countersfor each_copp_class in copp_counters_cleankeys()
if copp_counters_clean[each_copp_class][intfPacketCounters][dropPacketsrarr˓] = 0
copp_drops[host_name_clean][each_copp_class] = copp_drops[host_name_clean][each_copp_class][Drop Packets] = copp_
rarr˓counters_clean[each_copp_class][intfPacketCounters][dropPackets]
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
if not copp_drops[host_name_clean]del copp_drops[host_name_clean]
Section 5
if bool(errors)pprintpprint(errors)
if bool(copp_drops)pprintpprint(copp_drops)
42 Monitor Control Plane Drops 81
arista-python-web2py Documentation Release 001
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtEnter your username admin22sw2 ucopp-system-glean Drop Packets 103342722sw37 ucopp-system-default Drop Packets 1225931
ucopp-system-glean Drop Packets 1060622sw4 ucopp-system-glean Drop Packets 2228369
Final Script
Here is the final script that shows if there are any control plane packet drops in the network
Discover if there is any control plane drops in the network
Section 1
import pyeapiimport getpassimport pprint
Section 2
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []with open(file_switches) as readfile
for line in readfileswitchesappend(linestrip())
Section 3
Input Username and Password
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
Section 4
copp_drops = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
82 Chapter 4 Chapter 3 Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
Find the host name of the switch for reportinghost_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])copp_drops[host_name_clean] =
Execute the desired commandcopp_counters = nodeexecute([show policy-map interface control-plane copp-
rarr˓system-policy])copp_counters_clean = copp_counters[result][0][policyMaps][copp-system-
rarr˓policy][classMaps]
parse through each copp system class map and discover non drop countersfor each_copp_class in copp_counters_cleankeys()
if copp_counters_clean[each_copp_class][intfPacketCounters][dropPacketsrarr˓] = 0
copp_drops[host_name_clean][each_copp_class] = copp_drops[host_name_clean][each_copp_class][Drop Packets] = copp_
rarr˓counters_clean[each_copp_class][intfPacketCounters][dropPackets]
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
if not copp_drops[host_name_clean]del copp_drops[host_name_clean]
Section 5
if bool(errors)pprintpprint(errors)
if bool(copp_drops)pprintpprint(copp_drops)
42 Monitor Control Plane Drops 83
arista-python-web2py Documentation Release 001
84 Chapter 4 Chapter 3 Troubleshooting Use Cases
CHAPTER 5
Chapter 4 Capacity Planning Use Cases
bull Port Capacity
ndash Arista EOS Show Commands
ndash Algorithm
ndash Develop Script
ndash Final Script
bull Hardware Scalability Assessment
ndash Mac Address Table Scale
Arista EOS Show Command
Develop Script
ndash VRF Scale
Arista EOS Show Command
Develop Script
ndash ARP Scale
Arista EOS Show Command
Develop Script
ndash Routing Scale
Arista EOS Show Command
Develop Script
ndash TCAM Scale
Arista EOS Show Command
85
arista-python-web2py Documentation Release 001
Develop Script
ndash Hardware Scale
Exception Handling
ndash Final Script
We are going to develop scripts for a couple of capacity planning use cases First use case is to identify the availableport density and the unused transceivers in the network Second use case is to identify the usage of hardware resourcessuch as mac address arp route and tcam tables We introduce Pythonrsquos jsonrpc and functions in this chapter We alsoshow you how to explore the Arista EOS commands and the json output via graphical user interface
Port Capacity
The goal of this script is to identify the number of unused ports and transceivers in the network We are going tocollect the information and report it in the following format
unused_ports =
switch_host_name switch_model ltswitch_Modelgtdescription ltswitch descriptiongt10G
interface_type ltNumber of unusedrarr˓portsgt
interface_type ltNumber of unusedrarr˓portsgt
40G
interface_type ltNumber of unusedrarr˓portsgt
interface_type ltNumber of unusedrarr˓portsgt
switch_host_name switch_model ltswitch_Modelgtdescription ltswitch descriptiongt10G
interface_type ltNumber of unusedrarr˓portsgt
interface_type ltNumber of unusedrarr˓portsgt
40G
interface_type ltNumber of unusedrarr˓portsgt
interface_type ltNumber of unusedrarr˓portsgt
Under each switch we will report how many 10GE 40GE or 100GE ports are available There are different transceivertypes available and we will identify the different types and save the information
86 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
First we will write down the step by step commands to collect the data second we will develop an algorithm andfinally we will build the Python script using our framework
Arista EOS Show Commands
Step 1 Inventory
Model name and description provides additional insight when presenting unused ports Show inventory command canbe used to collect the model and switch description
Arista-switchshow inventory | json
systemInformation name DCS-7050SX-128description 96x 10Gb SFP+ + 8x 40Gb QSFP+ 2RUmfgDate 2015-12-21hardwareRev 0200serialNum JPE14080459
Step 2 Commands to collect the unused interfaces
ldquonotconnectrdquo option in ldquoshow interfaces statusrdquo shows the ports configured as ldquono shutdownrdquo but links are not upldquodisabledrdquo option shows the ports configured as ldquoshutdownrdquo
Arista-switch show interfaces status notconnect disabled | json
interfaceStatuses Ethernet8
vlanInformation interfaceMode bridgedvlanId 1interfaceForwardingModel bridged
bandwidth 10000000000interfaceType 10GBASE-SRdescription autoNegotiateActive falseduplex duplexFullautoNegotigateActive falselinkStatus notconnectlineProtocolStatus notPresent
Ethernet9
vlanInformation interfaceMode bridgedvlanId 1interfaceForwardingModel bridged
bandwidth 10000000000interfaceType Not Presentdescription autoNegotiateActive falseduplex duplexFullautoNegotigateActive falselinkStatus notconnectlineProtocolStatus notPresent
51 Port Capacity 87
arista-python-web2py Documentation Release 001
Algorithm
We are going to use the following algorithm to write the Python script
1 Create an empty dictionary for unused_ports unused_ports =
2 For each switch create a key value pair using the switch name as the key unused_ports[host_name] =
3 For each switch collect model and description and update unused_ports dictionary unused_ports[host_name]= ldquoModelrdquo ltmodelgt ldquoDescriptionrdquo ltdescriptiongt
4 For each switch we will collect ldquoshow interfaces status notconnect disabledrdquo output and parse through band-width and interface type values of each interface
5 We will take the bandwidth value of each interface and check if there is an entry in the dictionary underthe specific switch name If there is no entry for that particular bandwidth we will add a key valuepair entry for that bandwidth with the bandwidth as the key and an empty dictionary as the value un-used_ports[host_name][bandwidth] =
6 Next we will look at the interface type of that interface If there is no entry for that interface type withinunused_ports[host_name][bandwidth] we will create one with a key value pair Key is the interface type andthe value is an integer number ldquo1rdquo unused_ports[host_name][bandwidth][interfacetype] = 1
7 If there is an entry for that interface type we will increment the value by 1 un-used_ports[host_name][bandwidth][interfacetype] += 1
Develop Script
Prepare Create a new Python script using the framework we developed
Open the IDLE and create a new script and save as unused_portspy in your folder Copy the code from section 1 23 from our framework and paste it in this new script
Discover Unused Ports in the network
Section 1
import pyeapiimport getpassimport pprint
Section 2
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []with open(file_switches) as readfile
for line in readfileswitchesappend(linestrip())
Section 3
88 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
Input Username and Password
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
Steps 1 2 and 3 Since we are already familiar with pyeapi dictionary and for loop we can start section 4 with theusual for loop empty dictionary and pyeapi commands which are required for the first three steps of our algorithm
Step 1 Create an empty dictionary for unused_ports
Step 2 For each switch create a key value pair using the switch name as the key
Step 3 For each switch collect model and description and update unused_ports dictionary
Section 4
Create an Empty Dictionaryunused_ports = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
collect the hostname of the switch and create an entry in the dictionaryhost_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])unused_ports[host_name_clean] =
Collect the model name and device descriptionshow_inventory = nodeexecute([show inventory])model = str(show_inventory[result][0][systemInformation][name])description = str(show_inventory[result][0][systemInformation][
rarr˓description])unused_ports[host_name_clean] = Model model Description description
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
if bool(errors)pprintpprint(errors)
if bool(unused_ports)pprintpprint(unused_ports)
Dictionary structure for model and description from show inventory has been derived from command API explorerSo far we have used Python shell to explore the syntax Another method is to use command API explorer After youenable management api on the Arista switch you can access the switch via your browser with the URL httpsltIP-addressgt It will prompt you for user name and password After that you can type any EOS show command and click
51 Port Capacity 89
arista-python-web2py Documentation Release 001
ldquosubmit POST requestrdquo to view the output in json format
Save and run the script from IDLE (Run rarr Run Module)
gtgtgt ================================ RESTART ================================gtgtgtEnter your username admin22sw2 Description 96x 10Gb SFP+ + 8x 40Gb QSFP+ 2RU
Model DCS-7050SX-12822sw35 Description 64x QSFP+ 2RU Model DCS-7250QX-6422sw37 Description 64x QSFP+ 2RU Model DCS-7250QX-6422sw4 Description 96x 10Gb SFP+ + 8x 40Gb QSFP+ 2RU
Model DCS-7050SX-128
Step 4 For each switch we will collect ldquoshow interfaces status notconnect disabledrdquo and parse through bandwidthand interface type values of each interface
Before writing the script we will identify the dictionary structure for bandwidth and interface type using commandapi explorer
90 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
Section 4
Create an Empty Dictionaryunused_ports = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
collect the hostname of the switch and create an entry in the dictionaryhost_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])unused_ports[host_name_clean] =
Collect the model name and device descriptionshow_inventory = nodeexecute([show inventory])model = str(show_inventory[result][0][systemInformation][name])description = str(show_inventory[result][0][systemInformation][
rarr˓description])unused_ports[host_name_clean] = Model model Description description
Collect interfaces statussh_int_status = nodeexecute([show interfaces status notconnect disabled])sh_int_status_clean = sh_int_status[result][0][interfaceStatuses]
Identify unused ports and categorize based on bandwidth and transceiver typefor each_interface in sh_int_status_cleankeys()
bandwidth = sh_int_status_clean[each_interface][bandwidth]bandwidth_GE = str(bandwidth 1000000000) + GEinterface_type = str(sh_int_status_clean[each_interface][interfaceType])
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
51 Port Capacity 91
arista-python-web2py Documentation Release 001
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
if bool(errors)pprintpprint(errors)
if bool(unused_ports)pprintpprint(unused_ports)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtEnter your username admin22sw2 Description 96x 10Gb SFP+ + 8x 40Gb QSFP+ 2RU
Model DCS-7050SX-12822sw35 Description 64x QSFP+ 2RU Model DCS-7250QX-6422sw37 Description 64x QSFP+ 2RU Model DCS-7250QX-6422sw4 Description 96x 10Gb SFP+ + 8x 40Gb QSFP+ 2RU
Model DCS-7050SX-128gtgtgt bandwidth10000000000gtgtgt bandwidth_GE10GEgtgtgt interface_typeNot Present
Step 5 We will take the bandwidth value of each interface and check if there is an entry in the dictionary under thespecific switch name
If there is no entry for that particular bandwidth we will add a key value pair entry for that bandwidth with thebandwidth as the key and an empty dictionary as the value
Section 4
Create an Empty Dictionaryunused_ports = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
collect the hostname of the switch and create an entry in the dictionaryhost_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])unused_ports[host_name_clean] =
Collect the model name and device descriptionshow_inventory = nodeexecute([show inventory])model = str(show_inventory[result][0][systemInformation][name])description = str(show_inventory[result][0][systemInformation][
rarr˓description])
92 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
unused_ports[host_name_clean] = Model model Description description
Collect interfaces statussh_int_status = nodeexecute([show interfaces status notconnect disabled])sh_int_status_clean = sh_int_status[result][0][interfaceStatuses]
Identify unused ports and categorize based on bandwidth and transceiver typefor each_interface in sh_int_status_cleankeys()
bandwidth = sh_int_status_clean[each_interface][bandwidth]bandwidth_GE = str(bandwidth 1000000000) + GEinterface_type = str(sh_int_status_clean[each_interface][interfaceType])
check for bandwidth entry and add it if not thereif bandwidth_GE not in unused_ports[host_name_clean]
unused_ports[host_name_clean][bandwidth_GE] =
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
if bool(errors)pprintpprint(errors)
if bool(unused_ports)pprintpprint(unused_ports)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtEnter your username admin22sw2 0GE
10GE 40GE Description 96x 10Gb SFP+ + 8x 40Gb QSFP+ 2RUModel DCS-7050SX-128
22sw35 0GE 10GE 40GE Description 64x QSFP+ 2RUModel DCS-7250QX-64
22sw37 0GE 10GE Description 64x QSFP+ 2RUModel DCS-7250QX-64
22sw4 0GE 10GE 40GE Description 96x 10Gb SFP+ + 8x 40Gb QSFP+ 2RUModel DCS-7050SX-128
The reason we are seeing 0 GE is because of the management interface At the end of the script we will add a coupleof checks to skip management and dot1q sub interfaces
51 Port Capacity 93
arista-python-web2py Documentation Release 001
Management2 vlanInformation
interfaceMode routedinterfaceForwardingModel routed
bandwidth 0interfaceType 101001000description autoNegotiateActive trueduplex duplexUnknownautoNegotigateActive truelinkStatus notconnectlineProtocolStatus down
Step 6 and 7 Next we will look at the interface type of that interface If there is no entry for that interface type withinunused_ports[host_name][bandwidth] we will create one with a key value pair Key is the interface type and thevalue is an integer number ldquo1rdquo If there is an entry for that interface type we will increment the value by 1
Section 4
Create an Empty Dictionaryunused_ports = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
collect the hostname of the switch and create an entry in the dictionaryhost_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])unused_ports[host_name_clean] =
Collect the model name and device descriptionshow_inventory = nodeexecute([show inventory])model = str(show_inventory[result][0][systemInformation][name])description = str(show_inventory[result][0][systemInformation][
rarr˓description])unused_ports[host_name_clean] = Model model Description description
Collect interfaces statussh_int_status = nodeexecute([show interfaces status notconnect disabled])sh_int_status_clean = sh_int_status[result][0][interfaceStatuses]
Identify unused ports and categorize based on bandwidth and transceiver typefor each_interface in sh_int_status_cleankeys()
bandwidth = sh_int_status_clean[each_interface][bandwidth]bandwidth_GE = str(bandwidth 1000000000) + GEinterface_type = str(sh_int_status_clean[each_interface][interfaceType])
check for bandwidth entry and add it if not thereif bandwidth_GE not in unused_ports[host_name_clean]
unused_ports[host_name_clean][bandwidth_GE] =
check for interface type and add it if not there
94 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
if interface_type not in unused_ports[host_name_clean][bandwidth_GE]unused_ports[host_name_clean][bandwidth_GE][interface_type] = 1
elseunused_ports[host_name_clean][bandwidth_GE][interface_type] += 1
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
if bool(errors)pprintpprint(errors)
if bool(unused_ports)pprintpprint(unused_ports)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtEnter your username admin22sw2 0GE 101001000 1 NA 1
10GE 10GBASE-CR 110GBASE-SR 1Not Present 90dot1q-encapsulation 3
40GE Not Present 5Description 96x 10Gb SFP+ + 8x 40Gb QSFP+ 2RUModel DCS-7050SX-128
22sw35 0GE 101001000 110GE Not Present 21640GE Not Present 1Description 64x QSFP+ 2RUModel DCS-7250QX-64
22sw37 0GE 101001000 1 NA 110GE 40GBASE-CR4 3 Not Present 220Description 64x QSFP+ 2RUModel DCS-7250QX-64
22sw4 0GE 101001000 110GE 10GBASE-CR 1
10GBASE-SR 1Not Present 90dot1q-encapsulation 3
40GE Not Present 5Description 96x 10Gb SFP+ + 8x 40Gb QSFP+ 2RUModel DCS-7050SX-128
Step 8 Exclude non physical and management interfaces in the unused ports list
Section 4
Create an Empty Dictionaryunused_ports = errors =
51 Port Capacity 95
arista-python-web2py Documentation Release 001
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
collect the hostname of the switch and create an entry in the dictionaryhost_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])unused_ports[host_name_clean] =
Collect the model name and device descriptionshow_inventory = nodeexecute([show inventory])model = str(show_inventory[result][0][systemInformation][name])description = str(show_inventory[result][0][systemInformation][
rarr˓description])unused_ports[host_name_clean] = Model model Description description
Collect interfaces statussh_int_status = nodeexecute([show interfaces status notconnect disabled])sh_int_status_clean = sh_int_status[result][0][interfaceStatuses]
Identify unused ports and categorize based on bandwidth and transceiver typefor each_interface in sh_int_status_cleankeys()
if not in each_interface and Ethernet in each_interfacebandwidth = sh_int_status_clean[each_interface][bandwidth]bandwidth_GE = str(bandwidth 1000000000) + GEinterface_type = str(sh_int_status_clean[each_interface][
rarr˓interfaceType])
check for bandwidth entry and add it if not thereif bandwidth_GE not in unused_ports[host_name_clean]
unused_ports[host_name_clean][bandwidth_GE] =
check for interface type and add it if not thereif interface_type not in unused_ports[host_name_clean][bandwidth_GE]
unused_ports[host_name_clean][bandwidth_GE][interface_type] = 1else
unused_ports[host_name_clean][bandwidth_GE][interface_type] += 1
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
if bool(errors)pprintpprint(errors)
if bool(unused_ports)pprintpprint(unused_ports)
96 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
Final Script
Here is the final script that shows the inventory of unused ports and transceivers in the network
Discover Unused Ports in the network
Section 1
import pyeapiimport getpassimport pprint
Section 2
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []with open(file_switches) as readfile
for line in readfileswitchesappend(linestrip())
Section 3
Input Username and Password
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
Section 4
Create an Empty Dictionaryunused_ports = errors =
for switch in switchestry
Define API Connection Stringnode = pyeapiconnect(transport=https host=switch username=my_username
rarr˓password=my_password port=None)
collect the hostname of the switch and create an entry in the dictionaryhost_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])unused_ports[host_name_clean] =
Collect the model name and device descriptionshow_inventory = nodeexecute([show inventory])model = str(show_inventory[result][0][systemInformation][name])description = str(show_inventory[result][0][systemInformation][
rarr˓description])
51 Port Capacity 97
arista-python-web2py Documentation Release 001
unused_ports[host_name_clean] = Model model Description description
Collect interfaces statussh_int_status = nodeexecute([show interfaces status notconnect disabled])sh_int_status_clean = sh_int_status[result][0][interfaceStatuses]
Identify unused ports and categorize based on bandwidth and transceiver typefor each_interface in sh_int_status_cleankeys()
if not in each_interface and Ethernet in each_interfacebandwidth = sh_int_status_clean[each_interface][bandwidth]bandwidth_GE = str(bandwidth 1000000000) + GEinterface_type = str(sh_int_status_clean[each_interface][
rarr˓interfaceType])
check for bandwidth entry and add it if not thereif bandwidth_GE not in unused_ports[host_name_clean]
unused_ports[host_name_clean][bandwidth_GE] =
check for interface type and add it if not thereif interface_type not in unused_ports[host_name_clean][bandwidth_GE]
unused_ports[host_name_clean][bandwidth_GE][interface_type] = 1else
unused_ports[host_name_clean][bandwidth_GE][interface_type] += 1
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
Section 5
if bool(errors)pprintpprint(errors)
if bool(unused_ports)pprintpprint(unused_ports)
Hardware Scalability Assessment
The goal of this script is to identify the usage of hardware resources such as mac address arp route and tcam tablesWe are going to collect the information and report it in the following format
Verify_scale =
switch_host_name ldquoMAC Scalerdquo ltmac countgtldquoNo of VRFsrdquo ltno of VRFsgtldquoARP Scalerdquo ltarp countgt
ldquoRouting Scalerdquo ltroutesgtldquoTCAM Scalerdquo lttcam entriesgt
switch_host_name ldquoMAC Scalerdquo ltmac countgt
ldquoNo of VRFsrdquo ltno of VRFsgt
98 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
ldquoARP Scalerdquo ltarp countgtldquoRouting Scalerdquo ltroutesgt
ldquoTCAM Scalerdquo lttcam entriesgt
We are going to develop the scripts individually for each of these hardware resources using Python functions and thenwe will combine these functions in one script
Mac Address Table Scale
Arista EOS Show Command
Arista-switchshow mac address-table count | json
vlanCounts 115
dynamic 2unicast 1multicast 0
116
dynamic 0unicast 0multicast 0
4094
dynamic 0unicast 1multicast 0
1
dynamic 5unicast 0multicast 0
114
dynamic 2unicast 1multicast 0
Develop Script
If you have observed all the scripts we have developed so far we instantiate pyeapi object using connection parameters(IP address and authentication credentials) and using that object we execute various Arista EOS commands In thisscript we will instantiate the pyeapi object in the main script Then we will create a function and define all the logicrelated to identifying mac address scale inside that function First we will write this function to simply collect the macaddress table count from the switch and print
Open the IDLE create a new script and save as mac_scalepy in your folder
import pyeapiimport pprint
52 Hardware Scalability Assessment 99
arista-python-web2py Documentation Release 001
def mac_scale(node)mac = nodeexecute([show mac address-table count])pprintpprint(mac)
node = pyeapiconnect(transport=https host=1722817097 username=adminrarr˓password=admin port=None)mac_scale(node)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtuid u4439995728ujsonrpc u20uresult [uvlanCounts u1 udynamic 5
umulticast 0uunicast 0
u201 udynamic 0umulticast 0uunicast 1
u202 udynamic 0umulticast 0uunicast 1
u203 udynamic 0umulticast 0uunicast 1
We can also pass the result back to the main script and we can print from the main script
import pyeapiimport pprint
def mac_scale(node)mac = nodeexecute([show mac address-table count])return mac
node = pyeapiconnect(transport=https host=1722817097 username=adminrarr˓password=admin port=None)mac = mac_scale(node)pprintpprint(mac)
We can write the script to add the values of mac[rdquoresultrdquo][0][rdquovlanCountsrdquo][vlan][ ldquodynamicrdquo] from each vlan Letrsquosadd this logic in the function and return the total mac count instead of the per vlan mac count
import pyeapiimport pprint
def mac_scale(node)show_mac = nodeexecute([show mac address-table count])show_mac_clean = show_mac[result][0][vlanCounts]mac_count = 0for each_vlan in show_mac_cleankeys()
mac_count += show_mac_clean[each_vlan][dynamic]return mac_count
node = pyeapiconnect(transport=https host=1722817098 username=adminrarr˓password=admin port=None)
100 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
mac_count = mac_scale(node)pprintpprint(mac_count)
VRF Scale
Arista EOS Show Command
Arista-switchshow vrf | json This is an unconverted command
22sw4show vrfVrf RD Protocols State Interfaces
---------- ------------- --------------- -------------------- ---------------101 101101 ipv4ipv6 v4routing Ethernet97101
v6no routing Ethernet98101Vlan101
102 102102 ipv4ipv6 v4routing Ethernet97102v6no routing Ethernet98102
Vlan102103 103103 ipv4ipv6 v4routing Ethernet97103
v6no routing Ethernet98103Vlan103
104 104104 ipv4ipv6 v4routing Ethernet97104v6no routing Ethernet98104
Vlan104105 105105 ipv4ipv6 v4routing Ethernet97105
v6no routing Ethernet98105Vlan105
106 106106 ipv4ipv6 v4routing Ethernet97106v6no routing Ethernet98106
Vlan106
As you can see ldquoshow vrfrdquo command is not converted to json format We will explore how we can parse the requireddata for the commands that are not converted to json format
Develop Script
Open the IDLE create a new script and save as vrf_scalepy in your folder
import pyeapi
node = pyeapiconnect(transport=https host=1722817098 username=adminrarr˓password=admin port=None)
show_vrf = nodeexecute([show vrf])
Save and run the script
================================ RESTART ================================gtgtgt
Traceback (most recent call last)File UsersaneesGoogle Drivemy-scriptstestpy line 5 in ltmodulegtshow_vrf = nodeexecute([show vrf])
52 Hardware Scalability Assessment 101
arista-python-web2py Documentation Release 001
File LibraryPython27site-packagespyeapieapilibpy line 464 in executeresponse = selfsend(request)
File LibraryPython27site-packagespyeapieapilibpy line 385 in sendraise CommandError(code msg command_error=err output=out)
CommandError CLI command 1 of 1 show vrf failed unconverted command
Since the command is not converted to json format we have to collect the output using ldquotextrdquo format For that we aregoing to use the Python module called jsonrpclib instead of Aristarsquos pyeapi module
Install the jsonrpclib module using pip on your system
Listing 51 Apple Mac
anees~ anees$ pip install jsonrpclib
We will write a script using jsonrpc to collect the output for ldquoshow vrfrdquo in ldquotextrdquo format
from jsonrpclib import Server
node = Server(httpsadminadmin1722817098command-api)
show_vrf = noderunCmds(1 [show vrf] text)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgt
Traceback (most recent call last)File UsersaneesGoogle Drivemy-scriptstestpy line 5 in ltmodulegtshow_vrf = noderunCmds(1 [show vrf] text)
File LibraryPython27site-packagesjsonrpclibjsonrpcpy line 288 in __call_rarr˓_
Output truncated
SystemLibraryFrameworksPythonframeworkVersions27libpython27sslpy linerarr˓808 in do_handshake
self_sslobjdo_handshake()SSLError [SSL CERTIFICATE_VERIFY_FAILED] certificate verify failed (_sslc590)
SSLError is due to the fact that certification verification is enabled by default from Python 279 onwards For moreinformation refer PEP 476 We will disable SSL verification to move forward with the script
from jsonrpclib import Serverimport sslssl_create_default_https_context = ssl_create_unverified_context
node = Server(httpsadminadmin1722817098command-api)
show_vrf = noderunCmds(1 [show vrf] text)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtgtgtgt show_vrf
102 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
[uoutput u Vrf RD Protocols Staterarr˓Interfaces n
---------- ------------- --------------- -------------------- --------rarr˓------- n
101 101101 ipv4ipv6 v4routingrarr˓Ethernet97101 n
v6no routingrarr˓Ethernet98101 n
Vlan101rarr˓ n
102 102102 ipv4ipv6 v4routing Ethernet97rarr˓102 n
v6no routingrarr˓Ethernet98102 n
Vlan102rarr˓ n
103 103103 ipv4ipv6 v4routing Ethernet97rarr˓103 n
v6no routing
We have to use Pythonrsquos string parsing modules such as splitlines() and split() to parse line by line and word by wordto get the required field This method is often called as screen scrapping
As you can see from the ldquoshow vrfrdquo output it has some of the lines of data that are not necessary for our specific usecase This may make the parsing complicated as well So we use EOSrsquos include option to filter only the required lineand then we use Pythonrsquos string parsing modules to collect the necessary field
Arista-switchshow vrf | inc ipv4ipv6101 101101 ipv4ipv6 v4routing Ethernet97101102 102102 ipv4ipv6 v4routing Ethernet97102103 103103 ipv4ipv6 v4routing Ethernet97103104 104104 ipv4ipv6 v4routing Ethernet97104105 105105 ipv4ipv6 v4routing Ethernet97105106 106106 ipv4ipv6 v4routing Ethernet97106
Let us update the vrf_scalepy with the EOS command with the filters
from jsonrpclib import Serverimport sslssl_create_default_https_context = ssl_create_unverified_context
node = Server(httpsadminadmin1722817098command-api)
show_vrf = noderunCmds(1 [show vrf | include ipv4ipv6] text)
Save and run the script Then we are going to explore Pythonrsquos string parsing functions to derive the list of VRFs
gtgtgt ================================ RESTART ================================gtgtgtgtgtgt show_vrf[uoutput u 101 101101 ipv4ipv6 v4routingEthernet97101 n 102 102102 ipv4ipv6 v4routingEthernet97102 n 103 103103 ipv4ipv6 v4routingEthernet97103 n 104 104104 ipv4ipv6 v4routingEthernet97104 n 105 105105 ipv4ipv6 v4routingEthernet97105 n 106 106106 ipv4ipv6 v4routingEthernet97106 n 107 107107 ipv4ipv6 v4routingEthernet97107 n 108 108108 ipv4ipv6 v4routing
52 Hardware Scalability Assessment 103
arista-python-web2py Documentation Release 001
Ethernet97108 n 109 109109 ipv4ipv6 v4routingEthernet97109 n 110 110110 ipv4ipv6 v4routingEthernet97110 n 111 111111 ipv4ipv6 v4routingEthernet97111 n 112 112112 ipv4ipv6 v4routingEthernet97112 n 113 113113 ipv4ipv6 v4routingEthernet97113 n 114 114114 ipv4ipv6 v4routingEthernet97114 n 115 115115 ipv4ipv6 v4routingEthernet97115 n mgmt 100100 ipv4ipv6 v4no routingManagement1 n]gtgtgtgtgtgt show_vrf[0][output]u 101 101101 ipv4ipv6 v4routing Ethernet97101n 102 102102 ipv4ipv6 v4routing Ethernet97102n 103 103103 ipv4ipv6 v4routing Ethernet97103n 104 104104 ipv4ipv6 v4routing Ethernet97104n 105 105105 ipv4ipv6 v4routing Ethernet97105n 106 106106 ipv4ipv6 v4routing Ethernet97106n 107 107107 ipv4ipv6 v4routing Ethernet97107n 108 108108 ipv4ipv6 v4routing Ethernet97108n 109 109109 ipv4ipv6 v4routing Ethernet97109n 110 110110 ipv4ipv6 v4routing Ethernet97110n 111 111111 ipv4ipv6 v4routing Ethernet97111n 112 112112 ipv4ipv6 v4routing Ethernet97112n 113 113113 ipv4ipv6 v4routing Ethernet97113n 114 114114 ipv4ipv6 v4routing Ethernet97114n 115 115115 ipv4ipv6 v4routing Ethernet97115n mgmt 100100 ipv4ipv6 v4no routing Management1ngtgtgt show_vrf_clean = show_vrf[0][output]gtgtgtgtgtgt show_vrf_cleansplitlines()[u 101 101101 ipv4ipv6 v4routingEthernet97101 u 102 102102 ipv4ipv6 v4routingEthernet97102 u 103 103103 ipv4ipv6 v4routingEthernet97103 u 104 104104 ipv4ipv6 v4routingEthernet97104 u 105 105105 ipv4ipv6 v4routingEthernet97105 u 106 106106 ipv4ipv6 v4routingEthernet97106 u 107 107107 ipv4ipv6 v4routingEthernet97107 u 108 108108 ipv4ipv6 v4routingEthernet97108 u 109 109109 ipv4ipv6 v4routingEthernet97109 u 110 110110 ipv4ipv6 v4routingEthernet97110 u 111 111111 ipv4ipv6 v4routingEthernet97111 u 112 112112 ipv4ipv6 v4routingEthernet97112 u 113 113113 ipv4ipv6 v4routingEthernet97113 u 114 114114 ipv4ipv6 v4routingEthernet97114 u 115 115115 ipv4ipv6 v4routingEthernet97115 u mgmt 100100 ipv4ipv6 v4no routingManagement1 ]gtgtgtgtgtgt type(show_vrf_cleansplitlines())lttype listgtgtgtgtgtgtgt for each_line in show_vrf_cleansplitlines()
print each_line
101 101101 ipv4ipv6 v4routing Ethernet97101102 102102 ipv4ipv6 v4routing Ethernet97102
104 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
103 103103 ipv4ipv6 v4routing Ethernet97103104 104104 ipv4ipv6 v4routing Ethernet97104105 105105 ipv4ipv6 v4routing Ethernet97105106 106106 ipv4ipv6 v4routing Ethernet97106107 107107 ipv4ipv6 v4routing Ethernet97107108 108108 ipv4ipv6 v4routing Ethernet97108109 109109 ipv4ipv6 v4routing Ethernet97109110 110110 ipv4ipv6 v4routing Ethernet97110111 111111 ipv4ipv6 v4routing Ethernet97111112 112112 ipv4ipv6 v4routing Ethernet97112113 113113 ipv4ipv6 v4routing Ethernet97113114 114114 ipv4ipv6 v4routing Ethernet97114115 115115 ipv4ipv6 v4routing Ethernet97115mgmt 100100 ipv4ipv6 v4no routing Management1
gtgtgt each_lineu mgmt 100100 ipv4ipv6 v4no routing Management1 gtgtgtgtgtgt each_linesplit()[umgmt u100100 uipv4ipv6 uv4no urouting uManagement1]
gtgtgt each_linesplit()[0]umgmt
gtgtgt str(each_linesplit()[0])mgmt
gtgtgt for each_line in show_vrf_cleansplitlines()print str(each_linesplit()[0])
101102103104105106107108109110111112113114115mgmt
Now we have an idea on how to use jsonrpc to collect the show output in ldquotextrdquo format and parse the output usingPythonrsquos string processing modules
Letrsquos update our script with a function for VRF scale
import pprintfrom jsonrpclib import Serverimport sslssl_create_default_https_context = ssl_create_unverified_context
def vrf_scale(node)
52 Hardware Scalability Assessment 105
arista-python-web2py Documentation Release 001
show_vrf = noderunCmds(1 [show vrf | include ipv4ipv6] text)show_vrf_clean = show_vrf[0][output]
Create a list to store the VRFsvrfs = []for line in show_vrf_cleansplitlines()
fields = linesplit()vrfsappend(fields[0])
return vrfs
node = Server(httpsadminadmin1722817097command-api)vrfs = vrf_scale(node)
print (Number of VRFs s (len(vrfs)))print(VRFs List)pprintpprint(vrfs)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtNumber of VRFs 16VRFs List[u101u102u103u104u105u106u107u108u109u110u111u112u113u114u115umgmt]
ARP Scale
In VRF environment we have to calculate the number of ARP entries per VRF and add them to derive the total numberof ARP entries
Arista EOS Show Command
Arista-switch show ip arp vrf 101 summary | json
dynamicEntries 3ipV4Neighbors []notLearnedEntries 0totalEntries 3staticEntries 0
106 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
Arista-switch show ip arp vrf 107 summary | json
dynamicEntries 4ipV4Neighbors []notLearnedEntries 0totalEntries 4staticEntries 0
Develop Script
Since we already wrote a function called vrf_scale to identify the VRF names we can use that function to get the listof VRFs and then we can write a program to identify the ARP scale using that list
Create a new Python script from IDLE and save it as arp_scalepy
import pprintfrom jsonrpclib import Serverimport sslssl_create_default_https_context = ssl_create_unverified_context
def vrf_scale(node)show_vrf = noderunCmds(1 [show vrf | include ipv4ipv6] text)show_vrf_clean = show_vrf[0][output]vrfs = []for line in show_vrf_cleansplitlines()
fields = linesplit()vrfsappend(fields[0])
return vrfs
def arp_scale(node vrfs)for each_vrf in vrfs
show_arp = noderunCmds(1 [show ip arp vrf + each_vrf + summary])print show_arp[0][dynamicEntries]
Define Connection Attributesnode = Server(httpsadminadmin1722817097command-api)
Call VRF function and get the VRF namesvrfs = vrf_scale(node)
Call ARP functionarp_scale(node vrfs)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgt22222222
52 Hardware Scalability Assessment 107
arista-python-web2py Documentation Release 001
22222223
Let us complete the script by adding these number of ARP entries per VRF and return only the total number of ARPentries from the function
import pprintfrom jsonrpclib import Serverimport sslssl_create_default_https_context = ssl_create_unverified_context
def vrf_scale(node)show_vrf = noderunCmds(1 [show vrf | include ipv4ipv6] text)show_vrf_clean = show_vrf[0][output]vrfs = []for line in show_vrf_cleansplitlines()
fields = linesplit()vrfsappend(fields[0])
return vrfs
def arp_scale(node vrfs)arp_count = 0for each_vrf in vrfs
show_arp = noderunCmds(1 [show ip arp vrf + each_vrf + summary])arp_count += show_arp[0][dynamicEntries]
return arp_count
Define Connection Attributesnode = Server(httpsadminadmin1722817097command-api)
Call VRF function and get the VRF namesvrfs = vrf_scale(node)
Call ARP functionarp_count = arp_scale(node vrfs)
print (Total Number of ARP entries s (arp_count))
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtTotal Number of ARP entries 33
Routing Scale
In VRF environment we have to calculate number of routes per VRF and add them to derive the total number ofroutes
108 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
Arista EOS Show Command
Arista-switch show ip route vrf 101 summary | json
maskLen 24 6268 232 20760931 6
totalRoutes 208243staticNexthopGroup 0bgpCounts
bgpExternal 208229bgpInternal 0bgpTotal 208229
Arista-switch show ip route vrf 102 summary | json
maskLen 24 6268 232 931 6
totalRoutes 643staticNexthopGroup 0bgpCounts
bgpExternal 629bgpInternal 0bgpTotal 629
Develop Script
We are going to use the same logic as arp_scalepy script to calculate the route scale We use vrf_scale function to getthe list of VRFs and then by using that list we will write a function for calculating route scale
Create a new Python script from IDLE and save it as route_scalepy
import pprintfrom jsonrpclib import Serverimport sslssl_create_default_https_context = ssl_create_unverified_context
def vrf_scale(node)show_vrf = noderunCmds(1 [show vrf | include ipv4ipv6] text)show_vrf_clean = show_vrf[0][output]vrfs = []for line in show_vrf_cleansplitlines()
fields = linesplit()vrfsappend(fields[0])
return vrfs
def route_scale(node vrfs)route_count = 0
52 Hardware Scalability Assessment 109
arista-python-web2py Documentation Release 001
for each_vrf in vrfsshow_route = noderunCmds(1 [show ip route vrf + each_vrf + summary])route_count += show_route[0][totalRoutes]
return route_count
Define Connection Attributesnode = Server(httpsadminadmin1722817097command-api)
Call VRF function and get the VRF namesvrfs = vrf_scale(node)
Call Route functionroute_count = route_scale(node vrfs)
print (Total Number of IPv4 routes s (route_count))
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtTotal Number of IPv4 routes 234
TCAM Scale
Arista EOS Show Command
Arista-switch show platform trident tcam | json This is an unconverted command
Arista-switch show platform trident tcam=== TCAM summary for switch Linecard00 ===TCAM group 20 uses 1 entry and can use up to 767 more
MLAG uses 1 entriesTCAM group 10 uses 38 entries and can use up to 1754 more
Mlag control traffic uses 4 entriesCVX traffic uses 6 entriesL3 Control Priority uses 20 entriesIGMP Snooping Flooding uses 8 entries
TCAM group 11 uses 58 entries and can use up to 1734 moreACL Management uses 10 entriesL2 Control Priority uses 10 entriesStorm Control Management uses 2 entriesARP Inspection uses 1 entriesL3 Routing uses 35 entries
TCAM group 43 uses 1 entry and can use up to 767 moreVxlan EFP uses 1 entries
We will collect the ldquoshow platformrdquo output in ldquotextrdquo format and parse through the string to collect the number ofTCAM entries We can pipe the show output to receive only the interesting lines
Arista-switch show platform trident tcam | include TCAM groupTCAM group 20 uses 1 entry and can use up to 767 moreTCAM group 10 uses 38 entries and can use up to 1754 moreTCAM group 11 uses 58 entries and can use up to 1734 moreTCAM group 43 uses 1 entry and can use up to 767 more
110 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
Develop Script
Create a new Python script from IDLE and save it as tcam_scalepy
from jsonrpclib import Serverimport sslssl_create_default_https_context = ssl_create_unverified_context
node = Server(httpsadminadmin1722817098command-api)
show_tcam = noderunCmds(1 [show platform trident tcam | include TCAM group] textrarr˓)
Save and run the script
gtgt ================================ RESTART ================================gtgtgt
Traceback (most recent call last)File UsersaneesGoogle Drivemy-scriptstestpy line 8 in ltmodulegtshow_tcam = noderunCmds(1 [show platform trident tcam | include TCAM group]
rarr˓text)ProtocolError (1002 uCLI command 1 of 1 show platform trident tcam | include TCAMrarr˓group failed invalid command)
We need to understand why this command fails when we are running using jsonrpc Letrsquos open the command-apiexplorer for this switch and test the command
The above output shows that the command should run from privileged mode So we need to send the commandldquoenablerdquo in front of ldquoshow platform trident tcamrdquo command
52 Hardware Scalability Assessment 111
arista-python-web2py Documentation Release 001
Letrsquos update the tcam_scalepy script to run the ldquoshow platform trident tcamrdquo from privileged mode Then we willexplore the options to strip out the interesting data
from jsonrpclib import Serverimport sslssl_create_default_https_context = ssl_create_unverified_context
node = Server(httpsadminadmin1722817098command-api)
show_tcam = noderunCmds(1 [enable show platform trident tcam | include TCAMrarr˓group] text)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtgtgtgt show_tcam[uoutput u uoutput uTCAM group 20 uses 1 entry and can use up to 767rarr˓morenTCAM group 10 uses 38 entries and can use up to 1754 morenTCAM group 11rarr˓uses 58 entries and can use up to 1734 morenTCAM group 43 uses 1 entry and canrarr˓use up to 767 moren]gtgtgtgtgtgt show_tcam[1]uoutput uTCAM group 20 uses 1 entry and can use up to 767 morenTCAM group 10rarr˓uses 38 entries and can use up to 1754 morenTCAM group 11 uses 58 entries and canrarr˓use up to 1734 morenTCAM group 43 uses 1 entry and can use up to 767 morengtgtgtgtgtgt show_tcam[1][output]uTCAM group 20 uses 1 entry and can use up to 767 morenTCAM group 10 uses 38rarr˓entries and can use up to 1754 morenTCAM group 11 uses 58 entries and can use uprarr˓to 1734 morenTCAM group 43 uses 1 entry and can use up to 767 morengtgtgtgtgtgt show_tcam[1][output]splitlines()[uTCAM group 20 uses 1 entry and can use up to 767 more uTCAM group 10 uses 38rarr˓entries and can use up to 1754 more uTCAM group 11 uses 58 entries and can userarr˓up to 1734 more uTCAM group 43 uses 1 entry and can use up to 767 more]
112 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
gtgtgtgtgtgt for each_line in show_tcam[1][output]splitlines()
print each_line
TCAM group 20 uses 1 entry and can use up to 767 moreTCAM group 10 uses 38 entries and can use up to 1754 moreTCAM group 11 uses 58 entries and can use up to 1734 moreTCAM group 43 uses 1 entry and can use up to 767 moregtgtgtgtgtgt each_lineuTCAM group 43 uses 1 entry and can use up to 767 moregtgtgtgtgtgt each_linesplit()[uTCAM ugroup u43 uuses u1 uentry uand ucan uuse uup urarr˓to u767 umore]gtgtgtgtgtgt each_linesplit()[4]u1gtgtgt for each_line in show_tcam[1][output]splitlines()
print each_linesplit()[4]
138581gtgtgt tcam_count = 0gtgtgtgtgtgt for each_line in show_tcam[1][output]splitlines()
tcam_count += each_linesplit()[4]
Traceback (most recent call last)File ltpyshell49gt line 2 in ltmodulegttcam_count += each_linesplit()[4]
TypeError unsupported operand type(s) for += int and unicodegtgtgtgtgtgt for each_line in show_tcam[1][output]splitlines()
tcam_count += int(each_linesplit()[4])
gtgtgt tcam_count98
Letrsquos update the tcam_scalepy script with a Python function
from jsonrpclib import Serverimport sslssl_create_default_https_context = ssl_create_unverified_context
def tcam_scale(node)tcam_count = 0show_tcam = noderunCmds(1 [enable show platform trident tcam | include TCAM
rarr˓group] text)
for each_line in show_tcam[1][output]splitlines()tcam_count += int(each_linesplit()[4])
return tcam_count
Define Connection Attributes
52 Hardware Scalability Assessment 113
arista-python-web2py Documentation Release 001
node = Server(httpsadminadmin1722817097command-api)
Call tcam scale functiontcam_count = tcam_scale(node)
print (Total Number of TCAM Entries used is s (tcam_count))
Hardware Scale
We will consolidate all the five scalability use cases in one script Since we used jsonrpc for most of the scalabilityuse cases we will convert the mac scalability function to use jsonrpc instead of pyeapi We will also add a function tofind the hostname of the switch for storing the hardware scale under the switch name
Create a new Python script from IDLE and save it as hardware_scalepy
import pprintfrom jsonrpclib import Serverimport sslssl_create_default_https_context = ssl_create_unverified_context
def mac_scale(node)show_mac = noderunCmds(1 [show mac address-table count])show_mac_clean = show_mac[0][vlanCounts]mac_count = 0for each_vlan in show_mac_cleankeys()
mac_count += show_mac_clean[each_vlan][dynamic]return mac_count
def vrf_scale(node)show_vrf = noderunCmds(1 [show vrf | include ipv4ipv6] text)show_vrf_clean = show_vrf[0][output]vrfs = []for line in show_vrf_cleansplitlines()
fields = linesplit()vrfsappend(fields[0])
return vrfs
def arp_scale(node vrfs)arp_count = 0for each_vrf in vrfs
show_arp = noderunCmds(1 [show ip arp vrf + each_vrf + summary])arp_count += show_arp[0][dynamicEntries]
return arp_count
def route_scale(node vrfs)route_count = 0for each_vrf in vrfs
show_route = noderunCmds(1 [show ip route vrf + each_vrf + summary])route_count += show_route[0][totalRoutes]
return route_count
def tcam_scale(node)tcam_count = 0show_tcam = noderunCmds(1 [enable show platform trident tcam | include TCAM
rarr˓group] text)for each_line in show_tcam[1][output]splitlines()
114 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
tcam_count += int(each_linesplit()[4])return tcam_count
def hostname(node)host_name = noderunCmds(1 [show hostname])host_name_clean = str(host_name[0][hostname])return host_name_clean
Define Connection Attributes for jsonrpcnode = Server(httpsadminadmin1722817097command-api)
Call the hardware scale functionsname = hostname(node)mac_count = mac_scale(node)vrfs = vrf_scale(node)arp_count = arp_scale(node vrfs)route_count = route_scale(node vrfs)tcam_count = tcam_scale(node)
Store the values in a dictionaryverify_scale = verify_scale[name] = MAC Scale mac_count
Number of VRFs len(vrfs)Number of ARP Entries arp_countNumber of Routes route_countNumber of TCAM entries used tcam_count
Print the resultpprintpprint(verify_scale)
Save and the run the script
gtgtgt ================================ RESTART ================================gtgtgt22sw2 MAC Scale 5
Number of ARP Entries 33Number of Routes 234Number of TCAM entries used 98Number of VRFs 16
Now we have the logic for the script to find the hardware scale Next we will integrate this script with our frameworkWe will start with section 1 2 and 3 of the script framework
Script for hardware scalability assessment
Section 1
import pprintfrom jsonrpclib import Serverimport getpassimport sslssl_create_default_https_context = ssl_create_unverified_context
Section 2
52 Hardware Scalability Assessment 115
arista-python-web2py Documentation Release 001
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []with open(file_switches) as readfile
for line in readfileswitchesappend(linestrip())
Section 3
Input Username and Password
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
Letrsquos add all the scalability assessment functions in section 4A
Section 4A - Define Hardware Scalability Assessment Functions
def mac_scale(node)show_mac = noderunCmds(1 [show mac address-table count])show_mac_clean = show_mac[0][vlanCounts]mac_count = 0for each_vlan in show_mac_cleankeys()
mac_count += show_mac_clean[each_vlan][dynamic]return mac_count
def vrf_scale(node)show_vrf = noderunCmds(1 [show vrf | include ipv4ipv6] text)show_vrf_clean = show_vrf[0][output]vrfs = []for line in show_vrf_cleansplitlines()
fields = linesplit()vrfsappend(fields[0])
return vrfs
def arp_scale(node vrfs)arp_count = 0for each_vrf in vrfs
show_arp = noderunCmds(1 [show ip arp vrf + each_vrf + summary])arp_count += show_arp[0][dynamicEntries]
return arp_count
def route_scale(node vrfs)route_count = 0for each_vrf in vrfs
show_route = noderunCmds(1 [show ip route vrf + each_vrf + summary])route_count += show_route[0][totalRoutes]
return route_count
def tcam_scale(node)tcam_count = 0
116 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
show_tcam = noderunCmds(1 [enable show platform trident tcam | include TCAMrarr˓group] text)
for each_line in show_tcam[1][output]splitlines()tcam_count += int(each_linesplit()[4])
return tcam_count
def hostname(node)host_name = noderunCmds(1 [show hostname])host_name_clean = str(host_name[0][hostname])return host_name_clean
Our script for hardware scalability assessment is written for a single switch and we hard coded switch IP usernameand password in the script We need to redefine the jsonrpc connection object using variables
We are going to rewrite this statement
node = Server(httpsadminadmin1722817097command-api)
with variables as below
node = Server(https+my_username++my_password++switch+command-api)
Since we need to run this main script for all the switches we will create a for loop and place the main script that callsall the functions within the for loop
Section 4B - Main Script
verify_scale =
for switch in switches
Define Connection Attributes for jsonrpcnode = Server(https+my_username++my_password++switch+command-api)
Call the hardware scale functionsname = hostname(node)mac_count = mac_scale(node)vrfs = vrf_scale(node)arp_count = arp_scale(node vrfs)route_count = route_scale(node vrfs)tcam_count = tcam_scale(node)
Store the values in a dictionary
verify_scale[name] = MAC Scale mac_countNumber of VRFs len(vrfs)Number of ARP Entries arp_countNumber of Routes route_countNumber of TCAM entries used tcam_count
Finally we will print the result in the section 5 using pprint module
Section 5
Print the resultpprintpprint(verify_scale)
52 Hardware Scalability Assessment 117
arista-python-web2py Documentation Release 001
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtEnter your username admin22sw2 MAC Scale 5
Number of ARP Entries 33Number of Routes 234Number of TCAM entries used 98Number of VRFs 16
22sw35 MAC Scale 0Number of ARP Entries 2050Number of Routes 6307Number of TCAM entries used 510Number of VRFs 17
22sw37 MAC Scale 0Number of ARP Entries 2046Number of Routes 6273Number of TCAM entries used 510Number of VRFs 18
Exception Handling
One final feature we need to add in our script is exception handling Without the exception handling the script willterminate even if one switch is not accessible or any one EOS command is incorrect Our goal is to save the error in avariable and continue executing the program for other switches or EOS commands Also our goal is to differentiate ifit is a connectivity (or access) issue or incorrect EOS command
To understand the syntax of output error for inaccessible switch and incorrect EOS commands we will test the scriptfor those errors We create a small script called jsonexceptionpy with incorrect EOS command ldquoshow hostname1rdquo
import pprintfrom jsonrpclib import Serverimport sslssl_create_default_https_context = ssl_create_unverified_context
node = Server(httpsadminadmin1722817097command-api)host_name = noderunCmds(1 [show hostname1])
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgt
Traceback (most recent call last)File Usersaneesjsonexceptionpy line 9 in ltmodulegthost_name = noderunCmds(1 [show hostname1])
ProtocolError (1002 uCLI command 1 of 1 show hostname1 failed invalid command)
gtgtgt import jsonrpclibgtgtgt dir(jsonrpclib)[Config Fault History MultiCall ProtocolError Server __builtins__rarr˓ __doc__ __file__ __name__ __package__ __path__ config dumpsrarr˓history jsonclass jsonrpc loads]
Now we will update the script to handle this exception message ldquoProtocolErrorrdquo
118 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
import pprintfrom jsonrpclib import Server ProtocolErrorimport sslssl_create_default_https_context = ssl_create_unverified_context
trynode = Server(httpsadminadmin1722817097command-api)host_name = noderunCmds(1 [show hostname1])
except ProtocolError as eprint (Invalid EOS Command s (e))
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtInvalid EOS Command (1002 uCLI command 1 of 1 show hostname1 failed invalidrarr˓command)
In this example 1722817098 switch is accessible but username and password are not adminadmin
import pprintfrom jsonrpclib import Server ProtocolError
import sslssl_create_default_https_context = ssl_create_unverified_context
trynode = Server(httpsadminadmin1722817098command-api)host_name = noderunCmds(1 [show hostname])
except ProtocolError as eprint (Invalid EOS Command s (e))
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgt
Traceback (most recent call last)File Usersaneesjsonexceptionpy line 7 in ltmodulegthost_name = noderunCmds(1 [show hostname])
File LibraryPython27site-packagesjsonrpclibjsonrpcpy line 288 in __call_rarr˓_
return self__send(self__name args)File LibraryPython27site-packagesjsonrpclibjsonrpcpy line 237 in _
rarr˓requestresponse = self_run_request(request)
File LibraryPython27site-packagesjsonrpclibjsonrpcpy line 255 in _run_rarr˓request
verbose=self__verboseFile SystemLibraryFrameworksPythonframeworkVersions27libpython27
rarr˓xmlrpclibpy line 1280 in requestreturn selfsingle_request(host handler request_body verbose)
File SystemLibraryFrameworksPythonframeworkVersions27libpython27rarr˓xmlrpclibpy line 1328 in single_request
responsemsgProtocolError ltProtocolError for adminadmin1722817098command-api 401rarr˓Unauthorizedgt
52 Hardware Scalability Assessment 119
arista-python-web2py Documentation Release 001
As you can see ldquoexcept ProtocolErrorrdquo does not catch this exception of incorrect authentication and the script failsWe will create a except clause to catch all the other error messages
import pprintfrom jsonrpclib import Server ProtocolError
import sslssl_create_default_https_context = ssl_create_unverified_context
trynode = Server(httpsadminadmin1722817098command-api)host_name = noderunCmds(1 [show hostname])
except ProtocolError as eprint (Invalid EOS Command s (e))
exceptprint (eAPI Connection Error)
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgteAPI Connection Error
Letrsquos update the section 1 amp 4B of the script with the exception handling method
Section 1
import pprintfrom jsonrpclib import Server ProtocolErrorimport getpassimport sslssl_create_default_https_context = ssl_create_unverified_context
Section 4B - Main Program
verify_scale =
for switch in switchestry
Define Connection Attributes for jsonrpcnode = Server(https+my_username++my_password++switch+command-api
rarr˓)
Call the hardware scale functionsname = hostname(node)mac_count = mac_scale(node)vrfs = vrf_scale(node)arp_count = arp_scale(node vrfs)route_count = route_scale(node vrfs)tcam_count = tcam_scale(node)
Store the values in a dictionary
verify_scale[name] = MAC Scale mac_countNumber of VRFs len(vrfs)
120 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
Number of ARP Entries arp_countNumber of Routes route_countNumber of TCAM entries used tcam_count
except ProtocolError as everify_scale[switch] = Invalid EOS Command + str(e)
exceptverify_scale[switch] = eAPI Connection Error
Now let us test the script On the switchestxt file we are going to add an IP address ldquo1722817098rdquo which we knowthat having an authentication issue
172281709717228170981722817011517228170114
Save and run the script
gtgtgt ================================ RESTART ================================gtgtgtEnter your username admin1722817098 eAPI Connection Error22sw2 MAC Scale 5
Number of ARP Entries 33Number of Routes 234Number of TCAM entries used 98Number of VRFs 16
22sw35 MAC Scale 0Number of ARP Entries 2050Number of Routes 6307Number of TCAM entries used 510Number of VRFs 17
22sw37 MAC Scale 0Number of ARP Entries 2046Number of Routes 6273Number of TCAM entries used 510Number of VRFs 18
Final Script
Script for hardware scalability assessment
Section 1
import pprintfrom jsonrpclib import Server ProtocolErrorimport getpassimport sslssl_create_default_https_context = ssl_create_unverified_context
Section 2
52 Hardware Scalability Assessment 121
arista-python-web2py Documentation Release 001
Define file path and file names
file_path = UsersaneesGoogle Drivemy-scriptsfile_name_switches = switchestxtfile_switches = file_path + file_name_switches
Read the content of the file and save it in a List
switches = []with open(file_switches) as readfile
for line in readfileswitchesappend(linestrip())
Section 3
Input Username and Password
my_username = raw_input(Enter your username )my_password = getpassgetpass(Enter your password )
Section 4A - Define Hardware Scale Functions
def mac_scale(node)show_mac = noderunCmds(1 [show mac address-table count])show_mac_clean = show_mac[0][vlanCounts]mac_count = 0for each_vlan in show_mac_cleankeys()
mac_count += show_mac_clean[each_vlan][dynamic]return mac_count
def vrf_scale(node)show_vrf = noderunCmds(1 [show vrf | include ipv4ipv6] text)show_vrf_clean = show_vrf[0][output]vrfs = []for line in show_vrf_cleansplitlines()
fields = linesplit()vrfsappend(fields[0])
return vrfs
def arp_scale(node vrfs)arp_count = 0for each_vrf in vrfs
show_arp = noderunCmds(1 [show ip arp vrf + each_vrf + summary])arp_count += show_arp[0][dynamicEntries]
return arp_count
def route_scale(node vrfs)route_count = 0for each_vrf in vrfs
show_route = noderunCmds(1 [show ip route vrf + each_vrf + summary])route_count += show_route[0][totalRoutes]
return route_count
def tcam_scale(node)tcam_count = 0show_tcam = noderunCmds(1 [enable show platform trident tcam | include TCAM
rarr˓group] text)
122 Chapter 5 Chapter 4 Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
for each_line in show_tcam[1][output]splitlines()tcam_count += int(each_linesplit()[4])
return tcam_count
def hostname(node)host_name = noderunCmds(1 [show hostname])host_name_clean = str(host_name[0][hostname])return host_name_clean
Section 4B - Main Program
verify_scale =
for switch in switchestry
Define Connection Attributes for jsonrpcnode = Server(https+my_username++my_password++switch+command-api
rarr˓)
Call the hardware scale functionsname = hostname(node)mac_count = mac_scale(node)vrfs = vrf_scale(node)arp_count = arp_scale(node vrfs)route_count = route_scale(node vrfs)tcam_count = tcam_scale(node)
Store the values in a dictionary
verify_scale[name] = MAC Scale mac_countNumber of VRFs len(vrfs)Number of ARP Entries arp_countNumber of Routes route_countNumber of TCAM entries used tcam_count
except ProtocolError as everify_scale[switch] = Invalid EOS Command + str(e)
exceptverify_scale[switch] = eAPI Connection Error
Section 5
Print the resultpprintpprint(verify_scale)
52 Hardware Scalability Assessment 123
arista-python-web2py Documentation Release 001
124 Chapter 5 Chapter 4 Capacity Planning Use Cases
CHAPTER 6
Chapter 5 Web2Py Introduction
bull Understanding the functionality of the Tool
ndash Prepare the Tool
Add Switches
Categorize Switches
View Switches
ndash Capacity Planning
Port Capacity
ndash Network-wide Troubleshooting
Packet Drops
bull Web2Py Framework for the Tool
ndash Web2Py Framework
ndash Controller
ndash Views
I hope that you are comfortable in writing Python scripts to automate some of the network operational tasks by nowSo far we are writing the code and storing in a Python (py) file and run the script from terminal or from any Pythondevelopment environment Next step is to improve the usability of the scripts you have written so far How about ifyou can host all the Python scripts you have written so far in a web based tool This way you can access the toolanywhere from the network using your browser And you can share the tool with your team
As you start learning Python you will start to consume lot of Python modules written by the community With theweb based tool you have to install those modules only on the servers where you are hosting your Python scripts Allthe users who are consuming the tool does not have to install any of these modules and they donrsquot even need to havePython installed on their systems
125
arista-python-web2py Documentation Release 001
What do we need to do to build a web based tool and host the Python scripts We need a web framework such asWeb2Py or Django In this book we are going to use Web2Py as our web framework Since the fundamental principalof this book is to teach you Python and web2py by using the networking use cases we are going to explain the web2pyframework by using the tool we are going to build
We are going to build a tool like this
We will first understand the tool and then we will review how it is implemented using web2py framework Primarypurpose of the tool is to host your Python scripts As you can see that some of the scripts we created in the previouschapters were listed under network-wide troubleshooting and capacity planning sections We are going to add aprovision in the tool where you can manually add the IP addresses of the switches in the network Prepare the toolsection allows you to add IP addresses of the switches to the tool
Once you added the list of switches in the tool you can run your tasks under capacity planning and network-widetroubleshooting against those switches We will walk through the tool to better understand what we are going to buildusing web2py framework
Understanding the functionality of the Tool
The purpose of the tool is to run common network operational tasks across multiple Arista switches in the network
Prepare the Tool
Users should be able to add switches to the tool as and when they roll out new Arista switches in the network We arealso going to add an option to categorize switches based on site and role This gives an option to run the operationaltasks selectively on the switches belong to specific site and role
126 Chapter 6 Chapter 5 Web2Py Introduction
arista-python-web2py Documentation Release 001
Add Switches
Add switches will allow you to add the list of IP addresses of the Arista switches It requires username and passwordas well
After you provide all information and hit submit the script will collect switch name model and software version fromthe switches and then it stores the data in a file locally in the server We will show you the file name and the path whenwe show you how to build this tool later in this book The add switches task also displays the inventory of the switcheswe are adding in the output as below
61 Understanding the functionality of the Tool 127
arista-python-web2py Documentation Release 001
Categorize Switches
When you click ldquoCategorize Switchesrdquo the script will pull the inventory information of the switches we added in theldquoAdd Switchesrdquo task from the file stored in the server
128 Chapter 6 Chapter 5 Web2Py Introduction
arista-python-web2py Documentation Release 001
For each switch it will show the configured site and role For the switches added through ldquoAdd Switchesrdquo task thesetwo fields will be configured as none You can manually update each switch with corresponding site and role as peryour network design
View Switches
You can view the current inventory of the switches using ldquoView Switchesrdquo task
61 Understanding the functionality of the Tool 129
arista-python-web2py Documentation Release 001
Capacity Planning
We will just see how to run one of the tasks in this category to understand how the tool works
Port Capacity
When you click ldquoPort Capacityrdquo task it will prompt you for username password site and role If you want to run thetask on all the switches select All for both site and role fields Basically site and role fields give you the flexibility toselect the list of switches to which you want to run the task
130 Chapter 6 Chapter 5 Web2Py Introduction
arista-python-web2py Documentation Release 001
After you enter username password site and role the script will run against the switches based on your selection anddisplays the inventory of unused ports
Network-wide Troubleshooting
We will just see how to run one of the tasks in this category to understand how the tool works
Packet Drops
Almost all the use cases will prompt you the same form The data you need to provide is the username and passwordof the switches and then select the site and role
61 Understanding the functionality of the Tool 131
arista-python-web2py Documentation Release 001
After you enter username password site and role the script will run against the switches based on your selection Itdisplays the switch name and the interfaces where the packet drops are observed in the network
Web2Py Framework for the Tool
Web2Py can be downloaded from wwwweb2pycom and installed on most of the desktop and server operating systemsWeb2Py can be considered as server side application systems and you need a web tier to serve the applications toclients You can either use the Rocket WSGI web server that installed with Web2Py or you can leverage other webservers such as Apache In our example we will use Apache web server running on top of Ubuntu Linux operatingsystems to host our tool
Web2Py Framework
The web2py instance can server multiple applications Each application has a controller (defaultpy) where you writeyour Python scripts and a view which generates html page to interact with end users
132 Chapter 6 Chapter 5 Web2Py Introduction
arista-python-web2py Documentation Release 001
We are going to explore the web2py components of our specific application in this section Then we will show youhow to build the entire application from scratch in this book Let us explore the web2py components from the frontpage of our tool
As you can see from the URL our application name is ldquoeostoolrdquo controller name is ldquodefaultrdquo and the function nameis ldquoindexrdquo
62 Web2Py Framework for the Tool 133
arista-python-web2py Documentation Release 001
Once web2py is installed you can create multiple applications In our example we created an application calledldquoeostoolrdquo The below screenshot shows the list of applications created in our web2py instance
Using Web2Pyrsquos web administrative interface you can create applications Each application follows MVC (ModelView and Controller) framework
Controller
Default controller name for any web2py application is defaultpy This is where we write all our Python scripts Youcan edit defaultpy using web2py administrative interface
134 Chapter 6 Chapter 5 Web2Py Introduction
arista-python-web2py Documentation Release 001
In the previous chapters we created separate files for each of our use cases In web2py each of the use cases corre-sponds to a function in the default controller Port capacity hardware scalability assessment data plane and controlplane drops are all separate functions in the default controller
For example Add Switches in our tool is a function called add_inventory() which is in the controller defaultpy
Home page of the application ldquoeostoolrdquo is also a function called index() in the controller defaultpy
Views
The tool has to interact with the end users to collect the input and also to display the result of your Python scriptAs we know that each function in the default controller is your Python script performs specific network operationstask Each task requires user interface to collect the input from users and to display result We will create ldquoweb2pyviewrdquo for each function to facilitate the user interface View is nothing but a html file and the name of the html file
62 Web2Py Framework for the Tool 135
arista-python-web2py Documentation Release 001
is same as the name of the function in the default controller For example view of the function add_inventory() isadd_inventoryhtml
Whenever you need to create a new network operations task (Python script) you will create a separate function in thisdefaultpy controller and a separate view for your function Then you can add a link in the home page (indexhtml)
136 Chapter 6 Chapter 5 Web2Py Introduction
CHAPTER 7
Chapter 6 Web2Py Installation
bull Install Required Packages
ndash About My Linux
ndash Install Required Packages for Python Development
ndash Install Python modules
ndash Install Apache
ndash Create SSL Certificate
bull Install Web2Py
ndash Configure Apache to use mod_wsgi
bull Start Web2Py Service
ndash Verify Web2Py Portal
Web2py is an open source full stack framework You can download web2py from httpweb2pycominitdefaultdownload You can install it on Windows Apple Mac or any of the Linux distributions Installation instruction isdocumented here httpweb2pycombooksdefaultchapter2913deployment-recipes In this chapter we will showyou how to install web2py in Ubuntu Below is the quick installation steps to install and setup web2py on Ubunturunning Apache as the web server
Install Required Packages
About My Linux
aneesubuntu-web2py~$ uname -aLinux ubuntu-web2py 3160-30-generic 40~14041-Ubuntu SMP Thu Jan 15 174314 UTCrarr˓2015 x86_64 x86_64 x86_64 GNULinux
137
arista-python-web2py Documentation Release 001
aneesubuntu-web2py~$ python --versionPython 276
aneesubuntu-web2py$ ifconfigeth0 Link encapEthernet HWaddr 000c2998c832
inet addr17228170197 Bcast17228171255 Mask2552552540inet6 addr fe8020c29fffe98c83264 ScopeLinkUP BROADCAST RUNNING MULTICAST MTU1500 Metric1RX packets97480 errors0 dropped0 overruns0 frame0TX packets58948 errors0 dropped0 overruns0 carrier0collisions0 txqueuelen1000RX bytes111218578 (1112 MB) TX bytes10125393 (101 MB)
Install Required Packages for Python Development
aneesubuntu-anees-1~$ sudo apt-get install build-essential python-dev libsqlite3-rarr˓dev libreadline6-dev libgdbm-dev zlib1g-dev libbz2-dev sqlite3 zip libssl-dev
Install Python modules
aneesubuntu-web2py~$ sudo apt-get install python-pip
aneesubuntu-web2py~$ sudo pip install pyeapi
aneesubuntu-web2py~$ sudo pip install jsonrpc
Install Apache
aneesubuntu-web2py~$ sudo apt-get install apache2aneesubuntu-web2py~$ sudo apt-get install libapache2-mod-wsgianeesubuntu-web2py~$ sudo a2enmod wsgianeesubuntu-web2py~$ sudo a2enmod sslaneesubuntu-web2py~$ sudo a2enmod proxyaneesubuntu-web2py~$ sudo a2enmod proxy_httpaneesubuntu-web2py~$ sudo a2enmod headersaneesubuntu-web2py~$ sudo a2enmod expiresaneesubuntu-web2py~$ sudo a2enmod rewrite
Create SSL Certificate
aneesubuntu-web2py~$ sudo mkdir etcapache2sslaneesubuntu-web2py~$ sudo sh -c openssl genrsa 1024 gt etcapache2sslself_signedrarr˓key
aneesubuntu-web2py~$ sudo chmod 400 etcapache2sslself_signedkey
aneesubuntu-web2py~$ sudo sh -c openssl req -new -x509 -nodes -sha1 -days 365 -keyrarr˓etcapache2sslself_signedkey gt etcapache2sslself_signedcertYou are about to be asked to enter information that will be incorporated
138 Chapter 7 Chapter 6 Web2Py Installation
arista-python-web2py Documentation Release 001
into your certificate requestWhat you are about to enter is what is called a Distinguished Name or a DNThere are quite a few fields but you can leave some blankFor some fields there will be a default valueIf you enter the field will be left blank-----Country Name (2 letter code) [AU]USState or Province Name (full name) [Some-State]CALocality Name (eg city) []San JoseOrganization Name (eg company) [Internet Widgits Pty Ltd]AristaOrganizational Unit Name (eg section) []ServicesCommon Name (eg server FQDN or YOUR name) []ubuntu-web2pymylabcomEmail Address []adminmylabcom
aneesubuntu-web2py~$ sudo sh -c sudo openssl x509 -noout -fingerprint -text lt etcrarr˓apache2sslself_signedcert gt etcapache2sslself_signedinfo
Install Web2Py
aneesubuntu-web2py~$ cd homeaneesubuntu-web2pyhome$aneesubuntu-web2pyhome$ sudo mkdir www-dataaneesubuntu-web2pyhome$ cd www-data
aneesubuntu-web2pyhomewww-data$ sudo wget httpweb2pycomexamplesstaticrarr˓web2py_srczip
aneesubuntu-web2pyhomewww-data$ sudo unzip web2py_srczipaneesubuntu-web2pyhomewww-data$ sudo mv web2pyhandlerswsgihandlerpy web2pyrarr˓wsgihandlerpy
aneesubuntu-web2pyhomewww-data$ sudo chown -R www-datawww-data web2py
Configure Apache to use mod_wsgi
aneesubuntu-anees-1~$ cd etcapache2sites-available
aneesubuntu-anees-1etcapache2sites-available$ sudo vi web2pyconf
WSGIDaemonProcess web2py user=www-data group=www-data processes=1 threads=1
ltVirtualHost 80gt
RewriteEngine OnRewriteCond HTTPS =onRewriteRule ^() httpsSERVER_NAME$1 [RL]
CustomLog varlogapache2accesslog commonErrorLog varlogapache2errorlog
ltVirtualHostgt
ltVirtualHost 443gtSSLEngine on
72 Install Web2Py 139
arista-python-web2py Documentation Release 001
SSLCertificateFile etcapache2sslself_signedcertSSLCertificateKeyFile etcapache2sslself_signedkey
WSGIProcessGroup web2pyWSGIScriptAlias homewww-dataweb2pywsgihandlerpyWSGIPassAuthorization On
ltDirectory homewww-dataweb2pygtAllowOverride NoneRequire all deniedltFiles wsgihandlerpygt
Require all grantedltFilesgt
ltDirectorygt
AliasMatch ^([^]+)static(_[d]+[d]+[d]+)() homewww-dataweb2pyapplications$1static$2
ltDirectory homewww-dataweb2pyapplicationsstaticgtOptions -IndexesExpiresActive OnExpiresDefault access plus 1 hourRequire all granted
ltDirectorygt
CustomLog varlogapache2ssl-accesslog commonErrorLog varlogapache2errorlog
ltVirtualHostgt
wq
aneesubuntu-web2pyetcapache2sites-available$ cd aneesubuntu-web2pyetcapache2$ cd sites-enabledaneesubuntu-web2pyetcapache2sites-enabled$ sudo rm aneesubuntu-web2pyetcapache2sites-enabled$ sudo a2ensite web2py
aneesubuntu-anees-1etcapache2sites-available$ sudo service apache2 restart
Start Web2Py Service
aneesubuntu-anees-1etcapache2sites-available$ cd homewww-dataweb2py
aneesubuntu-anees-1homewww-dataweb2py $sudo -u www-data python -c from gluonwidget import console console()
aneesubuntu-anees-1homewww-dataweb2py $sudo -u www-data python -c from gluonmain import save_password save_password(raw_rarr˓input(admin password )443)
You will be prompted to setup the web2py admin password admin password
140 Chapter 7 Chapter 6 Web2Py Installation
arista-python-web2py Documentation Release 001
Verify Web2Py Portal
Verify the web2py portal by launching from your browser In our example we will launch web2py portal using the urlhttps17228170197
73 Start Web2Py Service 141
arista-python-web2py Documentation Release 001
142 Chapter 7 Chapter 6 Web2Py Installation
CHAPTER 8
Chapter 7 Creating a New Web2Py Application
bull Default Admin Page
bull Create a New Application
bull Edit the Application
We are going to create a new application and launch all our use cases through that application You can createmany applications and host it from single web2py instance For example you can build separate applications foryour network operations engineering and architecture groups The application which we are going to create is moresuitable for network operations team
Default Admin Page
When you install Web2Py it installs few applications by default One of them is an admin application which providesthe administrative interface to create modify develop and delete applications You can access the admin applica-tion from your browser by using the url httpsltweb-servergtadmindefaultindex It will prompt you for the adminpassword Use the password you set when you brought the web2py service up
143
arista-python-web2py Documentation Release 001
You will see the default applications created as part of the web2py installation
Create a New Application
You can create new applications from the admin interface Simply provide a name for your new applicationunder ldquoNew simple applicationrdquo section and click create In our example we use the application name asldquoArista_EOS_Toolrdquo
144 Chapter 8 Chapter 7 Creating a New Web2Py Application
arista-python-web2py Documentation Release 001
It will create the new application with default models controllers and views
You can view the newly created application by directly launching from your browser using the url httpsltweb-servergtArista_EOS_Tooldefaultindex You can also launch your application from admin interface Right click yourapplication and click ldquoopen Link in New Tabrdquo
82 Create a New Application 145
arista-python-web2py Documentation Release 001
This is the default web site created by web2py for your application
You can verify the folders and files created by web2py for your new application in the Ubuntu server You can see thedefault applications and your application in the ldquordquohomewww-dataweb2pyapplicationsrdquo folder
aneesubuntu-web2pyhomewww-dataweb2pyapplications$ pwdhomewww-dataweb2pyapplications
aneesubuntu-web2pyhomewww-dataweb2pyapplications$ lldrwxr-xr-x 8 www-data www-data 4096 Mar 14 1617 drwxr-xr-x 11 www-data www-data 4096 Mar 14 1617 drwxrwxr-x 14 www-data www-data 4096 Dec 29 1218 admindrwxrwxr-x 15 www-data www-data 4096 Dec 29 1220 Arista_EOS_Tooldrwxrwxr-x 14 www-data www-data 4096 Jan 29 2121 examples-rw-rw-r-- 1 www-data www-data 1 Dec 26 0458 __init__py-rw-r--r-- 1 www-data www-data 111 Dec 29 1218 __init__pycdrwxrwxr-x 14 www-data www-data 4096 Dec 29 1218 welcome
146 Chapter 8 Chapter 7 Creating a New Web2Py Application
arista-python-web2py Documentation Release 001
Within each application you can see the folders for model controllers and views
aneesubuntu-web2pyhomewww-dataweb2pyapplications$ cd Arista_EOS_Toolaneesubuntu-web2pyhomewww-dataweb2pyapplicationsArista_EOS_Tool$ lldrwxrwxr-x 15 www-data www-data 4096 Dec 29 1220 drwxr-xr-x 8 www-data www-data 4096 Mar 14 1617 -rw-rw-r-- 1 www-data www-data 55 Dec 26 0458 ABOUTdrwxr-xr-x 2 www-data www-data 4096 Dec 29 1219 cachedrwxrwxr-x 2 www-data www-data 4096 Dec 29 1221 controllersdrwxrwxr-x 2 www-data www-data 4096 Dec 26 0458 crondrwxr-xr-x 2 www-data www-data 4096 May 19 0922 databasesdrwxr-xr-x 2 www-data www-data 16384 May 19 0940 errors-rw-rw-r-- 1 www-data www-data 1 Dec 26 0458 __init__py-rw-r--r-- 1 www-data www-data 127 Dec 29 1220 __init__pycdrwxrwxr-x 2 www-data www-data 4096 Dec 26 0458 languages-rw-rw-r-- 1 www-data www-data 208 Dec 26 0458 LICENSEdrwxrwxr-x 2 www-data www-data 4096 Jan 29 1742 modelsdrwxrwxr-x 2 www-data www-data 4096 Dec 29 1220 modulesdrwxrwxr-x 2 www-data www-data 4096 Dec 26 0458 private-rw-r--r-- 1 www-data www-data 45009 Jun 9 1008 progresslog-rw-rw-r-- 1 www-data www-data 1510 Dec 26 0458 routesexamplepydrwxr-xr-x 20 www-data www-data 4096 Jun 6 0817 sessionsdrwxrwxr-x 6 www-data www-data 4096 Jun 9 1007 staticdrwxr-xr-x 2 www-data www-data 4096 Dec 29 1219 uploadsdrwxrwxr-x 3 www-data www-data 4096 Jan 29 1944 views
You can find the default controller defaultpy under the controllers folder
aneesubuntu-web2pyhomewww-dataweb2pyapplicationsArista_EOS_Tool$ cdrarr˓controllers
aneesubuntu-web2pyhomewww-dataweb2pyapplicationsArista_EOS_Toolcontrollers$ lldrwxrwxr-x 2 www-data www-data 4096 Dec 29 1221 drwxrwxr-x 15 www-data www-data 4096 Dec 29 1220 -rw-rw-r-- 1 www-data www-data 25689 Dec 26 0458 appadminpy-rw-rw-r-- 1 www-data www-data 33650 May 19 0945 defaultpy
You can find the views under the ldquoviewsdefaultrdquo folder
aneesubuntu-web2pyhomewww-dataweb2pyapplicationsArista_EOS_Toolcontrollers$rarr˓cd viewsdefaultaneesubuntu-web2pyhomewww-dataweb2pyapplicationsArista_EOS_Toolviewsdefault$rarr˓ll-rw-rw-r-- 1 www-data www-data 1660 Jun 9 1008 indexhtml
Edit the Application
We will remove the default scripts from the default controller and the view to start a clean empty application Thenfrom next chapter onwards we will start populating our network use cases in this application
Go to admin interface using the url httpsltweb-servergtadmindefaultindex Click the ldquoManagerdquo button and selectEdit
83 Edit the Application 147
arista-python-web2py Documentation Release 001
Click Edit which is right next to defaultpy controller
You will see the default functions created by web2py You should be able to see views (html files) for each of thefunction in this controller
148 Chapter 8 Chapter 7 Creating a New Web2Py Application
arista-python-web2py Documentation Release 001
Remove all the scripts in the defaultpy and just enter the below Python script
def index()return dict()
We have just created a function index() which does not have any logic and it just returns empty dictionary to the viewSave the script
83 Edit the Application 149
arista-python-web2py Documentation Release 001
Now we will cleanup the view (Arista_EOS_Toolviewsdefaultindexhtml) for the index() function Open the de-fault view for index() function from administrative interface On the left top you will see files toggle ndashgt Views ndashgtdefaultindexhtml
Delete the existing html scripts and just include the web2pyrsquos default layout layouthtml is hosted for ev-ery application you create through web2py You can find this file inside the views folder of your application(Arista_EOS_Toolviews)
150 Chapter 8 Chapter 7 Creating a New Web2Py Application
arista-python-web2py Documentation Release 001
Save the view and verify your blank application from browser httpltweb-servergtArista_EOS_TooldefaultindexThe web page displays only the default layout (layouthtml) which we included in the view
We have successfully created a blank application Let us move forward in hosting network operations use cases
83 Edit the Application 151
arista-python-web2py Documentation Release 001
152 Chapter 8 Chapter 7 Creating a New Web2Py Application
CHAPTER 9
Chapter 8 Web2Py - Prepare the Tool
bull Add Switches
ndash Algorithm
ndash Develop Script
Step 1 Create a New Function and a View for this task ldquoAdd Switchesrdquo
Step 2 Display a form to receive input for username password and IP addresses
Step 3 Collect inventory and store it in a dictionary
Step 4 Store the dictionary into a JSON file
bull View Switches
bull Categorize Switches
ndash Algorithm
ndash Develop Script
Step 1 Create a New Function and View for this task ldquoCategorize Switchesrdquo
Step 2 Read the content of inventoryjson and store it in a dictionary
Step 3 Build a web2py form from the dictionary
Step 4 Update site and role field in the dictionary
Step 5 Write the dictionary in the inventoryjson file
bull Summary
In this chapter we will create functions and views for the tasks in the ldquoPrepare the Toolrdquo category The purpose ofthese tasks in ldquoPrepare the Toolrdquo category is to provide an option to add the switches in the network manually andmaintain the inventory We will also provide an option to let the users to categorize the switches based on the locationand role Once the users added the switches in the tool then they can run any operational tasks against those switches
153
arista-python-web2py Documentation Release 001
The user will be able to run the tasks on all the switches or on the switches that belong to a specific site andor on theswitches with a specific role
We are going to create three tasks (Add Switches Categorize Switches and View Switches) in this section Each taskwill have one or more functions in the default controller and a corresponding view (html file)
Add Switches
ldquoAdd Switchesrdquo task allows the users to manually add the IP address of the switches into the tool The tool is goingto present a form to the end users where they can enter username password and the IP address of the switches Thenthe task is going to collect some of the basic information about the switches using pyeapi Finally the task will storethe inventory of these switches locally in the server The task will not save the username and the password anywherein the server
We are going to write a function add_inventory in the default controller of our application Since this will be our firstprogram using web2py we are going to spend more time in understanding how web2py works First we will write analgorithm for this task
Algorithm
We are going to collect the information and report it in the following format
Inventory = ldquoltswitch_IP_Addressgtrdquo ldquoHostnamerdquo ldquoltswitch_host_namegtrdquoldquoModelrdquo ldquoltswitch_modelgtrdquoldquoVersionrdquo ldquoltswitch_EOS_VersiongtrdquoldquoSerial Numberrdquo ldquoltSerial_NumbergtrdquoldquoSiterdquo ldquononerdquoldquoRolerdquo ldquononerdquo
The following Arista EOS commands are used to collect the above information
154 Chapter 9 Chapter 8 Web2Py - Prepare the Tool
arista-python-web2py Documentation Release 001
23sw35show hostname | json
fqdn 23sw35hostname 23sw35
23sw35show version | json
modelName DCS-7250QX-64-FinternalVersion 4155M-30540424155MsystemMacAddress 001c735d13e9serialNumber JPE14300059memTotal 8069500bootupTimestamp 146729919933memFree 4438208version 4155Marchitecture i386internalBuildId 43b3ce87-6eeb-48ce-bd2a-48e98def005ahardwareRevision 0103
Once we collect the data in a dictionary it is easy to display the content of the dictionary from the view and store it ina file in json format locally in the server
1 Create a New Function and a View for this task ldquoAdd Switchesrdquo
2 Display a form to input username password and IP address of the switches
3 Collect the inventory from the switches using pyeapi and store it in a dictionary
4 Store the dictionary in a json file called inventoryjson Save this file in the homewww-dataweb2pyapplicationsArista_EOS_Tooldatabases folder
Develop Script
Step 1 Create a New Function and a View for this task ldquoAdd Switchesrdquo
Go to admin interface using the url httpsltweb-servergtadmindefaultindex
Arista_EOS_Tool Manage ndashgt Edit
91 Add Switches 155
arista-python-web2py Documentation Release 001
Controllers defaultpy ndashgt Edit
Create a new function add_inventory()
156 Chapter 9 Chapter 8 Web2Py - Prepare the Tool
arista-python-web2py Documentation Release 001
Create a View for the function add_inventory
Click the ldquofiles togglerdquo on the top left
Click Create and select views from the drop down window
provide the file name with path ndashgt defaultadd_inventoryhtml
We are going to keep the default content inside the view Save this file
91 Add Switches 157
arista-python-web2py Documentation Release 001
You can verify the new function using the URL httpsltweb-servergtArista_EOS_Tooldefaultadd_inventory Sincethe function add_inventory is blank and it returns empty dictionary to the view The view shows the default layoutwhich shows the default web2py menu bar in the top of the screen and the title ldquoThis is the defaultadd_inventoryhtmltemplaterdquo
Step 2 Display a form to receive input for username password and IP addresses
There are few different ways to build forms in web2py We are going to create a form using web2pyrsquos SQL-FORMfactory We will define a form using SQLFORMfactory and assign it to a variable called form Then wewill return this variable to view using ldquoreturn dict(form=form)rdquo
As you can see there are three fields defined for our form The first string inside each Field() entry is the name of thevariable in which the values the user enters will be stored This should be unique within the form Rest of the stringswithin the Field() are optional
Edit the add_inventory function in the default controller
def add_inventory()form = SQLFORMfactory(
Field(username requires=IS_NOT_EMPTY())Field(password password requires=IS_NOT_EMPTY())Field(switchip text label=Switch IPs))
return dict(form=form)
We donrsquot have to update the view since we are going to display all variables from the function using the statementldquo=BEAUTIFY(response_vars)rdquo We are going to discuss more about views in chapter 11
Verify the updated function using the same URL httpsltweb-servergtArista_EOS_Tooldefaultadd_inventory
158 Chapter 9 Chapter 8 Web2Py - Prepare the Tool
arista-python-web2py Documentation Release 001
As you can see the field for switches is larger than for username and password This is because we declared this fieldas lsquotextrsquo when we define the form Similarly when you enter the field for password it wonrsquot display the content of thepassword This is because we declare this field as ldquopasswordrdquo when we define the form
You can change the display of the field different than the variable name by using label You can define the fields asmandatory using requires=IS_NOT_EMPTY()
You can refer the ldquoForms and validatorsrdquo chapter in the web2py documentation to learn more about web2py forms
Step 3 Collect inventory and store it in a dictionary
Once the user enters the username password IP addresses and submit the form the script should initiate the pyeapicall and collect the inventory from the switches The inventory will be stored in a dictionary and displayed to the enduser by returning the dictionary to the view
First we will understand how to accept the values of the form variables from the default controller So let us updateour add_inventory() function to display the value of the IP addresses after the user clicks the submit button
def add_inventory() Display form to input Username Password and Switch IP addressesform = SQLFORMfactory(
Field(username requires=IS_NOT_EMPTY())Field(password password requires=IS_NOT_EMPTY())Field(switchip text label=Switch IPs))
if the form is accepted collect the information from the switchesif formprocess()accepted
Initiate inventory with blank dictionaryinventory =
Since switch IPs are input as text with multiple IPs one per line We will convert the text into List with the list of switch IP addresses
91 Add Switches 159
arista-python-web2py Documentation Release 001
switchip_list = formvarsswitchipsplit(n)
For each IP in the list switchip_List collect the inventoryfor each_switch_ip in switchip_list
For each switch IP create empty directory with key as switch IPinventory[each_switch_ipstrip()] =
Return the inventory to Viewreturn dict(inventory=inventory)
Initially form will be returned to the viewreturn dict(form=form)
Save the defaultpy and verify your script using the URL httpsltweb-servergtArista_EOS_Tooldefaultadd_inventory Before that let us update the view (add_inventoryhtml) todisplay the title as ldquoAdd Switchesrdquo
extend layouthtmllth1gtAdd Switcheslth1gt=BEAUTIFY(response_vars)
When you enter httpsltweb-servergtArista_EOS_Tooldefaultadd_inventory add_inventory() function executesFirst the form variable is assigned with the fields defined using SQLFORMfactory() method When it executesldquoif formprocess()acceptedrdquordquo statement it bypasses the if clause since the form has not been submitted yet Then thelast statement of the add_inventory() function returns the form variable to the view add_inventoryhtml
Once you enter the username password switch IPs and submit ldquoif formprocess()acceptedrdquo clause is executed Sincewe define the variable inventory and return this dictionary to the view within the ldquoif formprocess()acceptedrdquo clausewe are seeing the content of the dictionary ldquoinventoryrdquo on the web page The values of the form fields are assignedinternally by formvarsltvariable_name_defined_in_the_fieldgt (for example formvarsusername formvarspasswordand formvarsswitchip)
Now we understand how to use web2py forms and display data using view let us update the add_inventory() functionto collect the inventory of the switches and store it in the dictionary
160 Chapter 9 Chapter 8 Web2Py - Prepare the Tool
arista-python-web2py Documentation Release 001
import pyeapi
Default Index for Home page of this tooldef index()
return dict()
Prepare the Tool Add Switchesdef add_inventory()
Display form to input Username Password and Switch IP addressesform = SQLFORMfactory(
Field(username requires=IS_NOT_EMPTY())Field(password password requires=IS_NOT_EMPTY())Field(switchip text label=Switch IPs))
if the form is accepted collect the information from the switchesif formprocess()accepted
Initiate inventory amp error with blank dictionaryinventory = errors =
Convert Switch IPs field from string to listswitchip_list = formvarsswitchipsplit(n)
For each IP in the List switchip_List apply your logicfor each_switch_ip in switchip_list
For each switch IP create empty directory with key as switch IPinventory[each_switch_ipstrip()] =
Connect to Switches and Collect Inventorytry
node = pyeapiconnect(transport=https host=each_switch_ipstrip()rarr˓username=formvarsusername password=formvarspassword port=None)
Collect the inventoryshow_hostname = nodeexecute([show hostname])hostname = str(show_hostname[result][0][hostname])
show_inventory = nodeexecute([show inventory])model = str(show_inventory[result][0][systemInformation][name])
show_version = nodeexecute([show version])version = str(show_version[result][0][version])serialnumber = str(show_version[result][0][serialNumber])
Save the collected data in the inventory dictionaryinventory[each_switch_ipstrip()] = hostname hostname model
rarr˓model serialnumber serialnumber version version site none rolerarr˓none
except pyeapieapilibConnectionErrorerrors[each_switch_ipstrip()] = ConnectionError unable to connect
rarr˓to eAPI
except pyeapieapilibCommandErrorerrors[each_switch_ipstrip()] = CommandError Check your EOS
rarr˓command syntax
Return the inventory to View
91 Add Switches 161
arista-python-web2py Documentation Release 001
return dict(errors=errors inventory=inventory)
Return form to viewreturn dict(form=form)
Save the config and verify the result
Step 4 Store the dictionary into a JSON file
Store the dictionary in JSON format and save under the folder homewww-dataweb2pyapplicationsArista_EOS_Tooldatabases The reason for storing the data in a file is that we willreuse this data (Switch IP addresses site and role) by all the uses cases created in this tool
First create a blank inventoryjson file in the server
aneesubuntu-web2py~$ cd homewww-dataweb2pyapplicationsArista_EOS_Toolrarr˓databases
aneesubuntu-web2pyhomewww-dataweb2pyapplicationsArista_EOS_Tooldatabases$rarr˓sudo sh -c echo gt inventoryjson
aneesubuntu-web2pyhomewww-dataweb2pyapplicationsArista_EOS_Tooldatabases$rarr˓sudo chown -R www-datawww-data inventoryjson
Update the script to store the content of the dictionary (inventory) into this file
162 Chapter 9 Chapter 8 Web2Py - Prepare the Tool
arista-python-web2py Documentation Release 001
import pyeapiimport json
Define inventory filefile_path = homewww-dataweb2pyapplicationsArista_EOS_Tooldatabasesfile_inventory = inventoryjsonfile = file_path + file_inventory
Default Index for Home page of this tooldef index()
return dict()
Prepare the Tool Add Switchesdef add_inventory()
Display form to input Username Password and Switch IP addressesform = SQLFORMfactory(
Field(username requires=IS_NOT_EMPTY())Field(password password requires=IS_NOT_EMPTY())Field(switchip text label=Switch IPs))
if the form is accepted collect the information from the switchesif formprocess()accepted
Initiate inventory amp error with blank dictionaryinventory = errors =
Convert Switch IPs field from string to listswitchip_list = formvarsswitchipsplit(n)
For each IP in the List switchip_List apply your logicfor each_switch_ip in switchip_list
For each switch IP create empty directory with key as switch IPinventory[each_switch_ipstrip()] =
Connect to Switches and Collect Inventorytry
node = pyeapiconnect(transport=https host=each_switch_ipstrip()rarr˓username=formvarsusername password=formvarspassword port=None)
Collect the inventoryshow_hostname = nodeexecute([show hostname])hostname = str(show_hostname[result][0][hostname])
show_inventory = nodeexecute([show inventory])model = str(show_inventory[result][0][systemInformation][name])
show_version = nodeexecute([show version])version = str(show_version[result][0][version])serialnumber = str(show_version[result][0][serialNumber])
Save the collected data in the inventory dictionaryinventory[each_switch_ipstrip()] = hostname hostname model
rarr˓model serialnumber serialnumber version version site none rolerarr˓none
except pyeapieapilibConnectionErrorerrors[each_switch_ipstrip()] = ConnectionError unable to connect
rarr˓to eAPI
91 Add Switches 163
arista-python-web2py Documentation Release 001
except pyeapieapilibCommandErrorerrors[each_switch_ipstrip()] = CommandError Check your EOS
rarr˓command syntax
Store the dictionary inventory in the json filewith open(file) as readfile
current_inventory = jsonload(readfile)current_inventoryupdate(inventory)
with open(file w) as writefilejsondump(current_inventory writefile)
Return the inventory to Viewreturn dict(errors=errors inventory=inventory)
Return form to viewreturn dict(form=form)
Verify your script using the URL httpsltweb-servergtArista_EOS_Tooldefaultadd_inventory The result will bedisplayed on the web page as before You can also check the content of the inventoryjson from the ubuntu server
aneesubuntu-web2py~$ cd homewww-dataweb2pyapplicationsArista_EOS_Toolrarr˓databasesaneesubuntu-web2pyhomewww-dataweb2pyapplicationsArista_EOS_Tooldatabases$ catrarr˓inventoryjson1722817098 serialnumber JPE14080459 hostname 22sw4 site nonerarr˓ version 4153F role none model DCS-7050SX-128 1722817097rarr˓serialnumber JPE14080457 hostname 22sw2 site none version 4rarr˓153F role none model DCS-7050SX-128 17228170114 serialnumberrarr˓ JPE14421537 hostname 22sw35 site none version 4153F rolerarr˓ none model DCS-7250QX-64 17228170115 serialnumberrarr˓JPE14402468 hostname 22sw37 site none version 4153F rolerarr˓none model DCS-7250QX-64aneesubuntu-web2pyhomewww-dataweb2pyrarr˓applicationsArista_EOS_Tooldatabases$
View Switches
ldquoView Switchesrdquo task allows the users to view the inventory of the switches stored in the tool We will write a newfunction called view_inventory() in the defaultpy to show the content of the inventoryjson file The logic is verysimple Read the json file into a dictionary and return that dictionary to the view
Edit the defaultpy file of the web2py application Arista_EOS_Tool and add this view_inventory() function
def view_inventory()with open(file) as readfile
inventory = jsonload(readfile)return dict(inventory=inventory)
Create a new web2py view view_inventoryhtml (defaultview_inventoryhtml) for this new function using web2pyeditor
extend layouthtmllth1gtView Switcheslth1gt=BEAUTIFY(response_vars)
164 Chapter 9 Chapter 8 Web2Py - Prepare the Tool
arista-python-web2py Documentation Release 001
Verify the new function using the URL httpsltweb-servergtArista_EOS_Tooldefaultview_inventory You shouldsee the result as below
Categorize Switches
After we added the switches manually we are going to classify the switches with based on the location and role Thisgives the user an option to run any scripts in this tool against specific set of switches For example we will be writinga script to find unused ports The user may want to run this script only on all leaf switches from a specific data centerLet us write an algorithm for this task
Algorithm
When a user adds switches using the add switches task the script will save the inventory information in the inven-toryjson file When it stores for the first time it assigns the value ldquoNonerdquo to the fields site and role
In this categorize switches task we are going to read and display the switches and its corresponding site and role in aweb2py form This will allow the users to assign site and role for all the switches in the inventoryjson file
1 Create a New Function and a View for this task ldquoCategorize Switchesrdquo
2 Read the content of inventoryjson file and assign it to a dictionary variable called inventory
3 Build a web2py form with the fields switchrsquos hostname site and role from the content of inventory dictionary
4 Once the user submit the form with the updated site and role fields update the dictionary variable ldquoinventoryrdquo
5 Write the dictionary inventory to the inventoryjson file
Develop Script
93 Categorize Switches 165
arista-python-web2py Documentation Release 001
Step 1 Create a New Function and View for this task ldquoCategorize Switchesrdquo
Edit the defaultpy file of the web2py application Arista_EOS_Tool and add this categorize_inventory() function
def categorize_inventory()return dict()
Create a new web2py view categorize_inventoryhtml (defaultcategorize_inventoryhtml) for this new function usingweb2py editor
extend layouthtmllth1gtCategorize Switcheslth1gt=BEAUTIFY(response_vars)
Step 2 Read the content of inventoryjson and store it in a dictionary
Update the categorize_inventory() function to read the inventoryjson file
def categorize_inventory()with open(file) as readfile
inventory = jsonload(readfile)return dict()
Step 3 Build a web2py form from the dictionary
This is an interesting step in this task We have to build a form with the fields hostname site and role from the contentof inventory variable First how did we create a form previously in this chapter
form = SQLFORMfactory(Field(username requires=IS_NOT_EMPTY())Field(password password requires=IS_NOT_EMPTY())Field(switchip text label=Switch IPs))
In the above form we know what fields need to be created But in this use case We have to create fields based on thecontent of inventoryjson file Our goal is to display field for each switch in the inventoryjson file Well actually wehave to create two fields (Site and Role) for each switch in the inventoryjson file So the number of fields depends onthe number of switches in the inventoryjson file
Web2py form allows to create a form using a list of fields You can create a list with the Field() objects and then youcan create SQLFORMfactory using that list
bull We are going to create two fields site and role for each switch
Field(siterdquo label=str(hostname)+ Site default=site)Field(role label=str(hostname)+ Role default=role)
ldquoSiterdquo and ldquoRolerdquo are the variable names for the data that the user is going to input We use switch hostnameas a label so that the user can classify the switch properly We donrsquot want to show blank field for Site and RoleWe want to populate the current Site and Role
bull We have to populate the above two fields for all the switches in the inventory file So we need somehow tocreate unique variable names for ldquoSiterdquo and ldquoRolerdquo We can use integers starting 1 For example site1 role1 forswitch1 site2 role2 for switch2 etc We will put all these fields in a list
166 Chapter 9 Chapter 8 Web2Py - Prepare the Tool
arista-python-web2py Documentation Release 001
Define an empty list for fieldsfields = []
Define a variable for integers which we will use to create unique variable namesfield_variable = 0
For each switch create fields and add it to the list fieldfor each_switch_ip in inventorykeys()
field_variable += 1
We will grab hostname site and role from current inventory We will use these in the Field variablehostname = inventory[each_switch_ip][hostname]site = inventory[each_switch_ip][site]role = inventory[each_switch_ip][role]
create variables and append to the listfieldsappend(Field(site+str(field_variable) label=str(hostname)+ Site
rarr˓default=site))fieldsappend(Field(role+str(field_variable) label=str(hostname)+ Role
rarr˓default=role))
bull Create a form using the fields list
form = SQLFORMfactory(fields)
Let us update the categorize_inventory() function with this new SQLFORM
def categorize_inventory()with open(file) as readfile
inventory = jsonload(readfile)
Define an empty list for fieldsfields = []
Define a variable for integers which we will use to create unique variable namesfield_variable = 0
For each switch create fields and add it to the list fieldfor each_switch_ip in inventorykeys()
field_variable += 1
We will grab hostname site and role from current inventory We will use these in the Field variablehostname = inventory[each_switch_ip][hostname]site = inventory[each_switch_ip][site]role = inventory[each_switch_ip][role]
create variables and append to the listfieldsappend(Field(site+str(field_variable) label=str(hostname)+ Site
rarr˓default=site))fieldsappend(Field(role+str(field_variable) label=str(hostname)+ Role
rarr˓default=role))
Create a form using the list fieldsform=SQLFORMfactory(fields)
return dict(form=form)
93 Categorize Switches 167
arista-python-web2py Documentation Release 001
Test the script using the URL httpsltweb-servergtArista_EOS_Tooldefaultcategorize_inventory
Step 4 Update site and role field in the dictionary
As we have unique variable names for Site and Role for each switch in inventoryjson file we will use the similar ldquoforlooprdquo statement to update the dictionary ldquoinventoryrdquo
def categorize_inventory()with open(file) as readfile
inventory = jsonload(readfile)
Define an empty list for fieldsfields = []
Define a variable for integers which we will use to create unique variable namesfield_variable = 0
For each switch create fields and add it to the list fieldfor each_switch_ip in inventorykeys()
field_variable += 1
We will grab hostname site and role from current inventory We will use these in the Field variablehostname = inventory[each_switch_ip][hostname]site = inventory[each_switch_ip][site]role = inventory[each_switch_ip][role]
create variables and append to the listfieldsappend(Field(site+str(field_variable) label=str(hostname)+ Site
rarr˓default=site))fieldsappend(Field(role+str(field_variable) label=str(hostname)+ Role
rarr˓default=role))
Create a form using the list fieldsform=SQLFORMfactory(fields)
168 Chapter 9 Chapter 8 Web2Py - Prepare the Tool
arista-python-web2py Documentation Release 001
Update dictionary based on the user inputif formprocess()accepted
field_variable = 0for each_switch_ip in inventorykeys()
field_variable += 1inventory[each_switch_ip][site] = formvars[site+str(field_variable)]inventory[each_switch_ip][role] = formvars[role+str(field_variable)]
return dict(inventory=inventory)
return dict(form=form)
Test the script using the URL httpsltweb-servergtArista_EOS_Tooldefaultcategorize_inventory
Step 5 Write the dictionary in the inventoryjson file
We will update the script to write the dictionary into the inventoryjson file
def categorize_inventory()with open(file) as readfile
inventory = jsonload(readfile)
Define an empty list for fieldsfields = []
Define a variable for integers which we will use to create unique variable namesfield_variable = 0
For each switch create fields and add it to the list fieldfor each_switch_ip in inventorykeys()
field_variable += 1
We will grab hostname site and role from current inventory We will use these in the Field variablehostname = inventory[each_switch_ip][hostname]site = inventory[each_switch_ip][site]role = inventory[each_switch_ip][role]
93 Categorize Switches 169
arista-python-web2py Documentation Release 001
create variables and append to the listfieldsappend(Field(site+str(field_variable) label=str(hostname)+ Site
rarr˓default=site))fieldsappend(Field(role+str(field_variable) label=str(hostname)+ Role
rarr˓default=role))
Create a form using the list fieldsform=SQLFORMfactory(fields)
Update dictionary based on the user inputif formprocess()accepted
field_variable = 0for each_switch_ip in inventorykeys()
field_variable += 1inventory[each_switch_ip][site] = formvars[site+str(field_variable)]inventory[each_switch_ip][role] = formvars[role+str(field_variable)]
Store the dictionary inventory in the json filewith open(file w) as writefile
jsondump(inventory writefile)
return dict(inventory=inventory)
return dict(form=form)
Test the script using the URL httpsltweb-servergtArista_EOS_Tooldefaultcategorize_inventory
You can use the view_inventory() function to verify whether the data is updated in the inventoryjson Testview_inventory() using httpsltweb-servergtArista_EOS_Tooldefaultview_inventory
170 Chapter 9 Chapter 8 Web2Py - Prepare the Tool
arista-python-web2py Documentation Release 001
Summary
We have completed three use cases under ldquoPreparing the Toolsrdquo section You can add more use cases as per yourrequirement For example you can create a use case for update inventory If any of the inventory data such ashostname software version or serial number (RMA) is changed and if you need to delete any of the switches fromthe inventory you can accomplish those with this use case
Each use case is a separate function within the default controller ldquodefaultpyrdquo and we access each of these use caseswith the URL httpsltweb-servergtArista_EOS_TooldefaultltName-of-the-functiongt
Later in this book we will create a home page with the links to all of these functions (use cases) The home page willbe accessible using the URL httpsltweb-servergtArista_EOS_Tooldefaultindex
94 Summary 171
arista-python-web2py Documentation Release 001
172 Chapter 9 Chapter 8 Web2Py - Prepare the Tool
CHAPTER 10
Chapter 9 Web2Py - Troubleshooting Use Cases
bull Data Plane Packet Drops
ndash Algorithm
ndash Develop Script
Step 1 Create a New Function and a View for this task ldquoPacket Dropsrdquo
Step 2 Display a form to get username password site and role
Step 3 Build Switch IP Address list
Step 4 Reuse the packet drops Python script
ndash Script Enhancements
Step 1 Create a function to create a web2py form
Step 2 Create a function to derive the list of switches
Step 3 Create a function for discovering interface drops
ndash Final Script
bull Control Plane Drops
bull Last 10 Sev 1-3 Log Messages
ndash Algorithm
ndash Develop Script
Step 1 Create a New Function and a View for this task ldquoshow logsrdquo
Step 2 Use the eos_form() and switches_list() function to display form
Step 3 Write the core logic of the script
ndash Final Script
173
arista-python-web2py Documentation Release 001
In this chapter we will create functions and views for the tasks in the ldquoNetwork-wide Troubleshootingrdquo category Thepurpose of these tasks is to perform some of the common network troubleshooting steps across the network using thetool
We are going to create three tasks (Data Plane Packet Drops Control Plane Drops and Last 10 Sev1-3 log messages)in this section Each task will have one or more functions in the default controller and a corresponding view (htmlfile)
Data Plane Packet Drops
We are going to write a function packet_drops() in the default controller of our web2py application Arista_EOS_toolWe have already written a Python script to discover network-wide packet drops in the earlier chapter in this book Weare going to reuse that script here The core logic is going to be the same The only change here is to receive the inputwith web2py form The input fields are username password site and role We are going to create a drop-down menuto show the sites and roles so that the users donrsquot have to type them
174 Chapter 10 Chapter 9 Web2Py - Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
Algorithm
We are going to use the below algorithm to write the script
1 Create a New Function and a View for this task ldquoPacket Dropsrdquo
2 Display a form to get username password site and role
3 Build Switch IP Address list
4 Reuse the packet drops Python script and returns the result to the view
Develop Script
Step 1 Create a New Function and a View for this task ldquoPacket Dropsrdquo
Go to admin interface using the url httpsltweb-servergtadmindefaultindex
Arista_EOS_Tool Manage ndashgt Edit
Controllers defaultpy ndashgt Edit
101 Data Plane Packet Drops 175
arista-python-web2py Documentation Release 001
Create a new function packet_drops()
def packet_drops()return dict()
Create a View for the function packet_drops
Click the ldquofiles togglerdquo on the top left
Click Create and select views from the drop down window
Provide the file name with path ndashgt defaultpacket_dropshtml
176 Chapter 10 Chapter 9 Web2Py - Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
We are going to create a default view with the header ldquoPacket Dropsrdquo Save this file
extend layouthtmllth1gtPacket Dropslth1gt=BEAUTIFY(response_vars)
Step 2 Display a form to get username password site and role
We know how to create a form with the static fields such as username and password using SQLFORMfactory()
form = SQLFORMfactory(Field(username requires=IS_NOT_EMPTY())Field(password password requires=IS_NOT_EMPTY()))
We need to create the fields for site and role with a drop-down menu The SQLFORMfactory() provides the optionusing IS_IN_SET() function to display drop-down menu The drop-down menu can list the content of Pythonrsquos datastructure called set First we will look at SQLFORMfactory()rsquos option for drop-down menu and then we will spendsome time in the Python interpreter to understand what is set
form = SQLFORMfactory(Field(username requires=IS_NOT_EMPTY())Field(password password requires=IS_NOT_EMPTY())Field(site requires=IS_IN_SET(sites))Field(role requires=IS_IN_SET(roles)))
The ldquositerdquo and ldquorolerdquo fields are going to show the drop-down menus with the list of sites and roles stored in the setldquositesrdquo and ldquorolesrdquo respectively Now we will explore Pythonrsquos data structure set We will create a list with duplicateentries and then we will convert the list as set
101 Data Plane Packet Drops 177
arista-python-web2py Documentation Release 001
anees~ anees$ pythongtgtgtgtgtgt sites = [All sjc atl lax sjc]gtgtgtgtgtgt sites[All sjc atl lax sjc]gtgtgtgtgtgt type(sites)lttype listgtgtgtgtgtgtgt sites = set([All sjc atl lax sjc])gtgtgtgtgtgt sitesset([sjc All lax atl])gtgtgtgtgtgt type(sites)lttype setgt
Before defining the SQLFORMfactory() we need to build the set sites and roles from the inventoryjson file We willread this file and pull site and role for each switch and add it to the set sites and roles Since sites and roles are setswe donrsquot have to worry about the duplicate entries of site and role from the inventoryjson file
Letrsquos start building this portion of the script step by step
def packet_drops() Read the inventoryjson file into a dictionarywith open(file) as readfile
inventory = jsonload(readfile)
Define set sites and roles with a single item Allsites = set([All])roles = set([All])
Read site and role of each switch and add it to the setfor each_switch in inventorykeys()
sitesadd(inventory[each_switch][site])rolesadd(inventory[each_switch][role])
return dict(sites=sites roles=roles)
Test your script using the URL httpsltweb-servergtArista_EOS_Tooldefaultpacket_drops
Letrsquos add the form and validate the display of the form
def packet_drops() Read the inventoryjson file into a dictionarywith open(file) as readfile
inventory = jsonload(readfile)
Define set sites and roles with a single item All
178 Chapter 10 Chapter 9 Web2Py - Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
sites = set([All])roles = set([All])
Read site and role of each switch and add it to the setfor each_switch in inventorykeys()
sitesadd(inventory[each_switch][site])rolesadd(inventory[each_switch][role])
Create a form using the setsform = SQLFORMfactory(
Field(username requires=IS_NOT_EMPTY())Field(password password requires=IS_NOT_EMPTY())Field(site requires=IS_IN_SET(sites))Field(role requires=IS_IN_SET(roles)))
return dict(form=form)
Test your script using the URL httpsltweb-servergtArista_EOS_Tooldefaultpacket_drops
Step 3 Build Switch IP Address list
Our goal is to derive the list of switch IP addresses from the inventoryjson file based on the selection of site and roleby the user One important point to remember here is that we provide an option ldquoAllrdquo in the site and role field of theform So we need to write an algorithm to derive the switch IP addresses Let us look at the inventory of the switchesbefore writing the algorithm
101 Data Plane Packet Drops 179
arista-python-web2py Documentation Release 001
Here is the algorithm we are going to use to build the list of switch IP addresses
bull Create an empty list called switches = [ ]
bull If the site = ldquoAllrdquo and the role = ldquoAllrdquo we need all the switch IP addresses from the inventoryjson file So wewill assign inventorykeys() to the list switches
bull If both the site and role are not ldquoAllrdquo we have to parse through site and role of each switch against the inputselected by the user
ndash If the site = ldquoAllrdquo and the role = (user selected) compare the role of each switch against the role selectedby the user
ndash If the site = (user selected) and the role = ldquoAll compare the site of each switch against the site selected bythe user
ndash If the site = (user selected) role = (user selected) compare the site and role of each switch against theinput selected by the user
Letrsquos update the packet_drops() function to derive switch ip addresses after the user submitted the form
def packet_drops() Read the inventoryjson file into a dictionarywith open(file) as readfile
inventory = jsonload(readfile)
Define set sites and roles with a single item Allsites = set([All])roles = set([All])
Read site and role of each switch and add it to the setfor each_switch in inventorykeys()
180 Chapter 10 Chapter 9 Web2Py - Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
sitesadd(inventory[each_switch][site])rolesadd(inventory[each_switch][role])
Create a form using the setsform = SQLFORMfactory(
Field(username requires=IS_NOT_EMPTY())Field(password password requires=IS_NOT_EMPTY())Field(site requires=IS_IN_SET(sites))Field(role requires=IS_IN_SET(roles)))
if formprocess()accepted Create a switch IP addresses list from site and role selectionswitches = []
compare the site and role between user selected and the values ininventory
if formvarssite == All and formvarsrole == Allswitches = inventorykeys()
elsefor each_switch in inventorykeys()
each_site = inventory[each_switch][site]each_role = inventory[each_switch][role]if formvarssite == All and formvarsrole == each_role
switchesappend(each_switch)elif (formvarssite == each_site and
formvarsrole == All)switchesappend(each_switch)
elif (formvarssite == each_site andformvarsrole == each_role)
switchesappend(each_switch)
Return the switches list to the view for verification purposereturn dict(switches=switches)
return dict(form=form)
Test your script using the URL httpsltweb-servergtArista_EOS_Tooldefaultpacket_drops Make sure you see theexpected switch IP addresses list by selecting various combinations of sites and roles
101 Data Plane Packet Drops 181
arista-python-web2py Documentation Release 001
Step 4 Reuse the packet drops Python script
We will use the packet drops Python script which we wrote in chapter 3 The only thing you should change in that scriptis to change the username and password variable in the pyeapiconnect() function You should use formvarsusernameand formvarspassword for username and password variable
def packet_drops() Read the inventoryjson file into a dictionarywith open(file) as readfile
inventory = jsonload(readfile)
Define set sites and roles with a single item Allsites = set([All])roles = set([All])
Read site and role of each switch and add it to the setfor each_switch in inventorykeys()
sitesadd(inventory[each_switch][site])rolesadd(inventory[each_switch][role])
Create a form using the setsform = SQLFORMfactory(
Field(username requires=IS_NOT_EMPTY())Field(password password requires=IS_NOT_EMPTY())Field(site requires=IS_IN_SET(sites))Field(role requires=IS_IN_SET(roles)))
if formprocess()accepted Create a switch IP addresses list from site and role selectionswitches = []
compare the site and role between user selected and the values ininventory
if formvarssite == All and formvarsrole == Allswitches = inventorykeys()
else
182 Chapter 10 Chapter 9 Web2Py - Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
for each_switch in inventorykeys()each_site = inventory[each_switch][site]each_role = inventory[each_switch][role]if formvarssite == All and formvarsrole == each_role
switchesappend(each_switch)elif (formvarssite == each_site and
formvarsrole == All)switchesappend(each_switch)
elif (formvarssite == each_site andformvarsrole == each_role)
switchesappend(each_switch)
Packet Drops Scriptinterface_drops = errors = for switch in switches
try Define API Connection Stringnode = pyeapiconnect(transport=https host=switch
username=formvarsusernamepassword=formvarspasswordport=None)
Execute the desired commandinterface_counters = nodeexecute(
[show interfaces counters discards])interface_counters_clean = interface_counters[
result][0][interfaces]host_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])interface_drops[host_name_clean] =
for interface in interface_counters_cleankeys()if Port-Channel not in interface
if interface_counters_clean[interface][inDiscards] orrarr˓interface_counters_clean[interface][outDiscards] = 0
interface_drops[host_name_clean][interface] = show_interface = nodeexecute(
[show interfaces + str(interface)])show_interface_clean = show_interface[result][0][
interfaces][interface][interfaceCounters]interface_drops[host_name_clean][interface][Interface
rarr˓Status] = show_interface[result][0][interfaces][interface][interfaceStatus
rarr˓]interface_drops[host_name_clean][interface][Line
rarr˓Protocol Status] = show_interface[result][0][interfaces][interface][
rarr˓lineProtocolStatus]
if interface_counters_clean[interface][inDiscards] = 0interface_drops[host_name_clean][
interface][inDiscards] = interface_drops[host_name_clean][interface][
rarr˓inDiscards][Total Discards] = interface_counters_
rarr˓clean[interface][inDiscards]interface_drops[host_name_clean][interface][
rarr˓inDiscards][
101 Data Plane Packet Drops 183
arista-python-web2py Documentation Release 001
Input Errors] = show_interface_clean[rarr˓inputErrorsDetail]
if interface_counters_clean[interface][outDiscards] =rarr˓0
interface_drops[host_name_clean][interface][outDiscards] =
interface_drops[host_name_clean][interface][rarr˓outDiscards][
Total Discards] = interface_counters_rarr˓clean[interface][outDiscards]
interface_drops[host_name_clean][interface][rarr˓outDiscards][
Output Errors] = show_interface_clean[rarr˓outputErrorsDetail]
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
if not interface_drops[host_name_clean]del interface_drops[host_name_clean]
Return the switches list to the view for verification purposereturn dict(errors=errors interface_drops=interface_drops)
return dict(form=form)
Test your script using the URL httpsltweb-servergtArista_EOS_Tooldefaultpacket_drops
Script Enhancements
At this point we know how to port our scripts to web2py To summarize we have three sections in the script
184 Chapter 10 Chapter 9 Web2Py - Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
1 Web2Py form to receive user input
2 Build Switch List based on the user input
3 The core logic for your Network Use case
The first two sections are going to be common for the most use cases We can create functions for these two sectionsso that we can re-use them for all our use cases We will also create a function for discovering the packet drops whichwill improve the readability and functionality of the script
Step 1 Create a function to create a web2py form
We will create a function called eos_form to build a web2py form We will call this form from the packet_dropsfunction
def eos_form() Read the inventoryjson file into a dictionarywith open(file) as readfile
inventory = jsonload(readfile)
Define set sites and roles with a single item Allsites = set([All])roles = set([All])
Read site and role of each switch and add it to the setfor each_switch in inventorykeys()
sitesadd(inventory[each_switch][site])rolesadd(inventory[each_switch][role])
Create a form using the setsform = SQLFORMfactory(
Field(username requires=IS_NOT_EMPTY())Field(password password requires=IS_NOT_EMPTY())Field(site requires=IS_IN_SET(sites))Field(role requires=IS_IN_SET(roles)))
return form
def packet_drops() Build Formform = eos_form()
if formprocess()accepted Create a switch IP addresses list from site and role selectionswitches = []
compare the site and role between user selected and the values ininventory
Step 2 Create a function to derive the list of switches
We will create a function switches_list to derive the list of switch IP addresses We will call this function from thepacket_drops function When we call we need to pass the user input so that switches_list function derives the listfrom the user provides values for site and role
101 Data Plane Packet Drops 185
arista-python-web2py Documentation Release 001
def switches_list(form_vars) Read the inventorywith open(file) as readfile
inventory = jsonload(readfile)
Define an empty listswitches = []
compare the site and role between user selected and the values ininventory
if form_varssite == All and form_varsrole == Allswitches = inventorykeys()
elsefor each_switch in inventorykeys()
each_site = inventory[each_switch][site]each_role = inventory[each_switch][role]if form_varssite == All and form_varsrole == each_role
switchesappend(each_switch)elif form_varssite == each_site and form_varsrole == All
switchesappend(each_switch)elif form_varssite == each_site and form_varsrole == each_role
switchesappend(each_switch)
return switches
def packet_drops() Build Formform = eos_form()
if formprocess()accepted Create a switch IP addresses list from site and role selectionswitches = switches_list(formvars)
Packet Drops Scriptinterface_drops = errors = for switch in switches
Step 3 Create a function for discovering interface drops
We will write a function for discovering interface drops and we will call this function from packet_drops function
def discover_packet_drops(node) Define empty dictionaryint_drops =
Execute the desired commandint_counters_raw = nodeexecute([show interfaces counters discards])int_counters = int_counters_raw[result][0][interfaces]
for interface in int_counterskeys()if Port-Channel not in interface
if (int_counters[interface][inDiscards] orint_counters[interface][outDiscards] = 0)
int_drops[interface] = show_int_raw = nodeexecute(
186 Chapter 10 Chapter 9 Web2Py - Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
[show interfaces + str(interface)])show_int = show_int_raw[result][0][
interfaces][interface][interfaceCounters]int_drops[interface][Interface Status] = show_int_raw[
result][0][interfaces][interface][interfaceStatus]int_drops[interface][Line Protocol Status] = show_int_raw[
result][0][interfaces][interface][lineProtocolStatus]
if int_counters[interface][inDiscards] = 0int_drops[interface][inDiscards] = int_drops[interface][inDiscards][
Total Discards] = int_counters[interface][inDiscards]int_drops[interface][inDiscards][
Input Errors] = show_int[inputErrorsDetail]
if int_counters[interface][outDiscards] = 0int_drops[interface][outDiscards] = int_drops[interface][outDiscards][
Total Discards] = int_counters[interface][outDiscards]int_drops[interface][outDiscards][
Output Errors] = show_int[outputErrorsDetail]return int_drops
def host_name(node)host_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])return host_name_clean
def packet_drops() Build Formform = eos_form()
if formprocess()accepted Create a switch IP addresses list from site and role selectionswitches = switches_list(formvars)
Packet Drops Scriptinterface_drops = errors = for switch in switches
try Define API Connection Stringnode = pyeapiconnect(transport=https host=switch
username=formvarsusernamepassword=formvarspasswordport=None)
Identify the switch hostname for reporting purposeswitchname = host_name(node)
Discover Interface Packet Dropsinterface_drops[switchname] = discover_packet_drops(node)
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
101 Data Plane Packet Drops 187
arista-python-web2py Documentation Release 001
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
if not interface_drops[switchname]del interface_drops[switchname]
Return the switches list to the view for verification purposereturn dict(errors=errors interface_drops=interface_drops)
return dict(form=form)
Final Script
The final script with all the functions is shown below
def eos_form() Read the inventoryjson file into a dictionarywith open(file) as readfile
inventory = jsonload(readfile)
Define set sites and roles with a single item Allsites = set([All])roles = set([All])
Read site and role of each switch and add it to the setfor each_switch in inventorykeys()
sitesadd(inventory[each_switch][site])rolesadd(inventory[each_switch][role])
Create a form using the setsform = SQLFORMfactory(
Field(username requires=IS_NOT_EMPTY())Field(password password requires=IS_NOT_EMPTY())Field(site requires=IS_IN_SET(sites))Field(role requires=IS_IN_SET(roles)))
return form
def switches_list(form_vars) Read the inventorywith open(file) as readfile
inventory = jsonload(readfile)
Define an empty listswitches = []
compare the site and role between user selected and the values ininventory
if form_varssite == All and form_varsrole == Allswitches = inventorykeys()
elsefor each_switch in inventorykeys()
each_site = inventory[each_switch][site]each_role = inventory[each_switch][role]if form_varssite == All and form_varsrole == each_role
188 Chapter 10 Chapter 9 Web2Py - Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
switchesappend(each_switch)elif form_varssite == each_site and form_varsrole == All
switchesappend(each_switch)elif form_varssite == each_site and form_varsrole == each_role
switchesappend(each_switch)
return switches
def discover_packet_drops(node) Define empty dictionaryint_drops =
Execute the desired commandint_counters_raw = nodeexecute([show interfaces counters discards])int_counters = int_counters_raw[result][0][interfaces]
for interface in int_counterskeys()if Port-Channel not in interface
if (int_counters[interface][inDiscards] orint_counters[interface][outDiscards] = 0)
int_drops[interface] = show_int_raw = nodeexecute(
[show interfaces + str(interface)])show_int = show_int_raw[result][0][
interfaces][interface][interfaceCounters]int_drops[interface][Interface Status] = show_int_raw[
result][0][interfaces][interface][interfaceStatus]int_drops[interface][Line Protocol Status] = show_int_raw[
result][0][interfaces][interface][lineProtocolStatus]
if int_counters[interface][inDiscards] = 0int_drops[interface][inDiscards] = int_drops[interface][inDiscards][
Total Discards] = int_counters[interface][inDiscards]int_drops[interface][inDiscards][
Input Errors] = show_int[inputErrorsDetail]
if int_counters[interface][outDiscards] = 0int_drops[interface][outDiscards] = int_drops[interface][outDiscards][
Total Discards] = int_counters[interface][outDiscards]int_drops[interface][outDiscards][
Output Errors] = show_int[outputErrorsDetail]return int_drops
def host_name(node)host_name = nodeexecute([show hostname])host_name_clean = str(host_name[result][0][hostname])return host_name_clean
def packet_drops() Build Formform = eos_form()
if formprocess()accepted
101 Data Plane Packet Drops 189
arista-python-web2py Documentation Release 001
Create a switch IP addresses list from site and role selectionswitches = switches_list(formvars)
Packet Drops Scriptinterface_drops = errors = for switch in switches
try Define API Connection Stringnode = pyeapiconnect(transport=https host=switch
username=formvarsusernamepassword=formvarspasswordport=None)
Identify the switch hostname for reporting purposeswitchname = host_name(node)
Discover Interface Packet Dropsinterface_drops[switchname] = discover_packet_drops(node)
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
if not interface_drops[switchname]del interface_drops[switchname]
Return the switches list to the view for verification purposereturn dict(errors=errors interface_drops=interface_drops)
return dict(form=form)
Control Plane Drops
This is going to be a very simple script We have already written functions for form and switch list We have alsowritten script for control plane drops in the chapter 3
We will write a new function called discover_copp_drops() in defaultpy and re-use the script for the control planedrops that we wrote in the chapter 3 Then we will create another function called controlplane_drops() in defaultpy tobuild our use-case by using all the three functions
def discover_copp_drops(node) Define empty dictionarycopp_drops =
Execute the desired commandcopp_counters_raw = nodeexecute(
[show policy-map interface control-plane copp-system-policy])copp_counters = copp_counters_raw[result][0][
policyMaps][copp-system-policy][classMaps]
parse through each copp system class map and discover non drop countersfor each_copp_class in copp_counterskeys()
if (copp_counters[each_copp_class][intfPacketCounters]
190 Chapter 10 Chapter 9 Web2Py - Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
[dropPackets] = 0)copp_drops[each_copp_class] = copp_drops[each_copp_class][Drop Packets] = copp_counters[
each_copp_class][intfPacketCounters][dropPackets]
return copp_drops
def controlplane_drops() Build Formform = eos_form()
if formprocess()accepted Create a switch IP addresses list from site and role selectionswitches = switches_list(formvars)
CoPP Drops Scriptcopp_drops = errors = for switch in switches
try Define API Connection Stringnode = pyeapiconnect(transport=https host=switch
username=formvarsusernamepassword=formvarspasswordport=None)
Identify the switch hostname for reporting purposeswitchname = host_name(node)
Discover CoPP Dropscopp_drops[switchname] = discover_copp_drops(node)
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
if not copp_drops[switchname]del copp_drops[switchname]
Return the switches list to the view for verification purposereturn dict(errors=errors copp_drops=copp_drops)
return dict(form=form)
Create a View for the function controlplane_drops
Click the ldquofiles togglerdquo on the top left
Click Create and select views from the drop down window
Provide the file name with path ndashgt defaultcontrolplane_dropshtml
We are going to create a default view with the header ldquoControl Plane Dropsrdquo Save this file
extend layouthtmllth1gtControl Plane Dropslth1gt
102 Control Plane Drops 191
arista-python-web2py Documentation Release 001
=BEAUTIFY(response_vars)
Test your script using the URL httpsltweb-servergtArista_EOS_Tooldefaultcontrolplane_drops
Last 10 Sev 1-3 Log Messages
The goal of this script is to collect the last 10 severity 1-3 messages from the switches It will be helpful when youare troubleshooting a network-wide problem and quickly run this command and look at the severity 1-3 logs on theswitches within the network We have not written a script for this logic previously in this book So we will first explorethe logic through IDLE and then we will port the logic into this tool
Letrsquos login to an Arista switch and explore the required EOS commands
Switch show logging | json This is an unconverted command
switch show logging 10 errors
Jan 9 105557 22sw2 ribd-vrf-102[27895] BGP-3-NOTIFICATION sent to neighbor 10rarr˓1020211 (AS 99) 40 (Hold Timer Expired ErrorUnspecified) 0 bytes
Jan 9 105557 22sw2 ribd-vrf-111[28249] BGP-3-NOTIFICATION sent to neighbor 10rarr˓1021111 (AS 99) 40 (Hold Timer Expired ErrorUnspecified) 0 bytes
Before looking at the algorithm letrsquos take a look at the basic Python script to pull the logs from the switch Since theshow log is not json converted we will use the jsonrpclib with ldquotextrdquo format and we parse the data using the stringparsing methods
import pprintfrom jsonrpclib import Serverimport sslssl_create_default_https_context = ssl_create_unverified_context
node = Server(httpsadminadmin1722817097command-api)
show_log = noderunCmds(1 [show logging 10 errors] text)show_log_clean = show_log[0][output]
pprintpprint(show_log_clean)
Save and run this script
192 Chapter 10 Chapter 9 Web2Py - Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
gtgtgt ================================ RESTART ================================gtgtgtuJan 9 105557 22sw2 ribd-vrf-102[27895] BGP-3-NOTIFICATION sent to neighbor 10rarr˓1020211 (AS 99) 40 (Hold Timer Expired ErrorUnspecified) 0 bytesnJan 9rarr˓105557 22sw2 ribd-vrf-111[28249] BGP-3-NOTIFICATION sent to neighbor 1010211rarr˓11 (AS 99) 40 (Hold Timer Expired ErrorUnspecified) 0 bytesn
Algorithm
We will build the script directly from web2py editor We are going to write a function switch_logs() in the defaultcontroller of our web2py application Arista_EOS_tool We are going to use the below algorithm to write the script
1 Create a New Function and a View for this task ldquoshow logsrdquo
2 Use the eos_form() and switches_list() function to display form
3 Write the core logic of the script
1 Create a list for the ldquologgingrdquo levels 0 to 3 which are emergencies alerts critical and errors
2 For each ldquologgingrdquo level within the list collect the show logging 10 ltlogging_levelgt output
3 For each logging level parse through the required output and store under the corresponding switch entry withinthe dictionary
4 Return the dictionary show_log to the view
Develop Script
Step 1 Create a New Function and a View for this task ldquoshow logsrdquo
Create a new function switch_logs() in the defaultpy controller
def switch_logs()return dict()
Create a view switch_logshtml under viewsdefault folder
extend layouthtmllth1gtDisplay Logs with Severity 0 to 3 lth1gt=BEAUTIFY(response_vars)
Step 2 Use the eos_form() and switches_list() function to display form
We have written two functions eos_form() and switches_list() in the previous use case We will use those functionswith this script to display form and derive IP addresses of the switches from the user selection
def switch_logs() Build Formform = eos_form()
if formprocess()accepted Create a switch IP addresses list from site and role selectionswitches = switches_list(formvars)
103 Last 10 Sev 1-3 Log Messages 193
arista-python-web2py Documentation Release 001
return dict(form=form)
Step 3 Write the core logic of the script
Since we are going to use jsonrpclib we need to install that module in the Linux system where we are hosting theweb2py application
aneesubuntu-web2py~$ sudo pip2 install jsonrpclibDownloadingunpacking jsonrpclib
Downloading jsonrpclib-017targzRunning setuppy (pathtmppip_build_rootjsonrpclibsetuppy) egg_info for
rarr˓package jsonrpclib
Installing collected packages jsonrpclibRunning setuppy install for jsonrpclib
Successfully installed jsonrpclibCleaning up
The algorithm for the core logic is shown below
1 Create a list for the ldquologgingrdquo levels 0 to 3 which are emergencies alerts critical and errors
2 For each ldquologgingrdquo level within the list collect the show logging 10 ltlogging_levelgt output
3 For each logging level parse through the required output and store under the corresponding switch entry withinthe dictionary
Import the neccessary python modulesimport pyeapiimport jsonfrom jsonrpclib import Server ProtocolError
def hostname(node)host_name = noderunCmds(1 [show hostname])host_name_clean = str(host_name[0][hostname])return host_name_clean
def collect_logs(node) Define an empty listshow_log = []
Create a list for the logging levels 0 to 3severity_level = [emergencies alerts critical errors]
For each logging level listed in the list collect the show logfor each_sev_level in severity_level
show_log_cli_raw = noderunCmds(1 [show logging 10 + each_sev_level] text)
show_log_cli = show_log_cli_raw[0][output]show_logappend(show_log_cli)
return show_log
194 Chapter 10 Chapter 9 Web2Py - Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
def switch_logs()form = eos_form()if formprocess()accepted
form_vars = formvarsswitches = switches_list(form_vars)
Define dictionariesshow_log =
for switch in switchesnode = Server(https + form_varsusername + +
form_varspassword + + switch +command-api)
Collect hostname for reporting purposename = hostname(node)
Collect the logsshow_log[name] = collect_logs(node)
return dict(show_log=show_log)
return dict(form=form)
Test your script using the URL httpsltweb-servergtArista_EOS_Tooldefaultswitch_logs
We are going to add three things to complete this script
1 Add Exception Handing
2 Display log entry one per line for clean view of the output
3 Add a logic to delete the switch entries if there are no logs
def collect_logs(node) Define an empty listshow_log = []
Create a list for the logging levels 0 to 3severity_level = [emergencies alerts critical errors]
For each logging level listed in the list collect the show logfor each_sev_level in severity_level
show_log_cli_raw = noderunCmds(1 [show logging 10 +each_sev_level] text)
show_log_cli = show_log_cli_raw[0][output]for line in show_log_clisplitlines()
103 Last 10 Sev 1-3 Log Messages 195
arista-python-web2py Documentation Release 001
show_logappend(line)
return show_log
def switch_logs()form = eos_form()if formprocess()accepted
form_vars = formvarsswitches = switches_list(form_vars)
Define dictionariesshow_log = errors =
for switch in switchestry
node = Server(https + form_varsusername + +form_varspassword + + switch +command-api)
Collect hostname for reporting purposename = hostname(node)
Collect the logsshow_log[name] = collect_logs(node)
If there are no logs delete the entry for the switchif not show_log[name]
del show_log[name]
except ProtocolError as eerrors[switch] = Invalid EOS Command + str(e)
excepterrors[switch] = eAPI Connection Error
return dict(errors=errors show_log=show_log)
return dict(form=form)
Test your script using the URL httpsltweb-servergtArista_EOS_Tooldefaultswitch_logs
Final Script
The final script with all the functions is shown below
196 Chapter 10 Chapter 9 Web2Py - Troubleshooting Use Cases
arista-python-web2py Documentation Release 001
Import the neccessary python modulesfrom jsonrpclib import Server ProtocolError
def hostname(node)host_name = noderunCmds(1 [show hostname])host_name_clean = str(host_name[0][hostname])return host_name_clean
def collect_logs(node) Define an empty listshow_log = []
Create a list for the logging levels 0 to 3severity_level = [emergencies alerts critical errors]
For each logging level listed in the list collect the show logfor each_sev_level in severity_level
show_log_cli_raw = noderunCmds(1 [show logging 10 +each_sev_level] text)
show_log_cli = show_log_cli_raw[0][output]for line in show_log_clisplitlines()
show_logappend(line)
return show_log
def switch_logs()form = eos_form()if formprocess()accepted
form_vars = formvarsswitches = switches_list(form_vars)
Define dictionariesshow_log = errors =
for switch in switchestry
node = Server(https + form_varsusername + +form_varspassword + + switch +command-api)
Collect hostname for reporting purposename = hostname(node)
Collect the logsshow_log[name] = collect_logs(node)
If there are no logs delete the entry for the switchif not show_log[name]
del show_log[name]
except ProtocolError as eerrors[switch] = Invalid EOS Command + str(e)
except
103 Last 10 Sev 1-3 Log Messages 197
arista-python-web2py Documentation Release 001
errors[switch] = eAPI Connection Error
return dict(errors=errors show_log=show_log)
return dict(form=form)
198 Chapter 10 Chapter 9 Web2Py - Troubleshooting Use Cases
CHAPTER 11
Chapter 10 Web2Py - Capacity Planning Use Cases
bull Port Capacity
ndash Develop Script
Step 1 Create a new Function and a View for this task ldquoPort Capacityrdquo
Step 2 Use the eos_form() and switches_list() function to display form
Step 3 Create a function to discover the unused ports
bull Hardware Scale
bull Summary
We have created a solid framework using web2py in the previous two sections ldquoPrepare the Toolrdquo and ldquoTroubleshootingUse Casesrdquo This section should be pretty straightforward to create the use cases for Capacity Planning with theframework And thatrsquos the goal of this book Once we created the framework you should be able to add plenty of usecases by spending time only on the core logic of your requirement than spending time on the web2py framework
In this chapter we are going to write two use cases that are Port Capacity and Hardware Tables Capacity
199
arista-python-web2py Documentation Release 001
Port Capacity
The goal of this use case is to find the unused ports and transceivers in the network We have already created the Pythonscript for this use case earlier in this book From web2py perspective when the user accesses the Port Capacity linkfrom the home page it should show the same form that asks for username password site and role
Once the user provided the required information the script collects the information about the unused ports and displaysthe result
200 Chapter 11 Chapter 10 Web2Py - Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
We are going to use the following steps to write the script
Develop Script
1 Create a new Function and a View for this task ldquoPort Capacityrdquo
2 Use the eos_form() and switches_list() function to display form
3 Create a function to discover the unused ports
Step 1 Create a new Function and a View for this task ldquoPort Capacityrdquo
Create a new function port_capacity() in the defaultpy controller
def port_capacity()return dict()
Create a view port_capacityhtml under viewsdefault folder
extend layouthtmllth1gtPort Capacity lth1gt=BEAUTIFY(response_vars)
Step 2 Use the eos_form() and switches_list() function to display form
We have written two functions eos_form() and switches_list() in the previous chapter We will use those functionswith this script to display form and derive IP addresses of the switches from the user selection
def port_capacity() Build Formform = eos_form()
111 Port Capacity 201
arista-python-web2py Documentation Release 001
if formprocess()accepted Create a switch IP addresses list from site and role selectionswitches = switches_list(formvars)
return dict(form=form)
Step 3 Create a function to discover the unused ports
We will create a new function discover_unused_ports() and reuse the script that we wrote in chapter 4 Then we willcall this function from the port_capacity() function
def discover_unused_ports(node) Define a dictionary for unused_portsunused_ports =
Collect the model name and device descriptionshow_inventory = nodeexecute([show inventory])model = str(show_inventory[result][0][systemInformation][name])description = str(show_inventory[result][0][systemInformation][
description])unused_ports = Model model Description description
Collect interfaces statussh_int_status_raw = nodeexecute([
show interfaces status notconnect disabled])sh_int_status = sh_int_status_raw[result][0][interfaceStatuses]
for each_interface in sh_int_statuskeys()bandwidth = sh_int_status[each_interface][bandwidth]bandwidth_GE = str(bandwidth 1000000000) + GEinterface_type = str(sh_int_status[each_interface][interfaceType])
check for bandwidth entry and add it if not thereif bandwidth_GE not in unused_ports
unused_ports[bandwidth_GE] =
check for interface type and add it if not thereif interface_type not in unused_ports[bandwidth_GE]
unused_ports[bandwidth_GE][interface_type] = 1else
unused_ports[bandwidth_GE][interface_type] += 1
return unused_ports
def port_capacity()form = eos_form()if formprocess()accepted
switches = switches_list(formvars)
Create an Empty Dictionaryunused_ports = errors =
for switch in switches
202 Chapter 11 Chapter 10 Web2Py - Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
try Define API Connection Stringnode = pyeapiconnect(transport=https host=switch
username=form_varsusernamepassword=form_varspasswordport=None)
Collect hostname for reporting purposeswitchname = host_name(node)
Discover Unused Portsunused_ports[switchname] = discover_unused_ports(node)
If there are no unused ports delete the entry for the switchif not unused_ports[switchname]
del unused_ports[switchname]
except pyeapieapilibConnectionErrorerrors[switch] = ConnectionError unable to connect to eAPI
except pyeapieapilibCommandErrorerrors[switch] = CommandError Check your EOS command syntax
return dict(errors=errors unused_ports=unused_ports)
return dict(form=form)
Test your script using the URL httpsltweb-servergtArista_EOS_Tooldefaultport_capacity
111 Port Capacity 203
arista-python-web2py Documentation Release 001
Hardware Scale
We have built four use cases in the web2py By now you should be comfortable to build use cases in web2py byleveraging the existing functions and Python scripts In this case we are going to copy all the functions we created forhardware table scalability in chapter 4 in the web2pyrsquos defaultpy controller
def mac_scale(node)show_mac = noderunCmds(1 [show mac address-table count])show_mac_clean = show_mac[0][vlanCounts]mac_count = 0for each_vlan in show_mac_cleankeys()
mac_count += show_mac_clean[each_vlan][dynamic]return mac_count
def vrf_scale(node)show_vrf = noderunCmds(1 [show vrf | include ipv4ipv6] text)show_vrf_clean = show_vrf[0][output]vrfs = []for line in show_vrf_cleansplitlines()
fields = linesplit()vrfsappend(fields[0])
return vrfs
204 Chapter 11 Chapter 10 Web2Py - Capacity Planning Use Cases
arista-python-web2py Documentation Release 001
def arp_scale(node vrfs)arp_count = 0for each_vrf in vrfs
show_arp = noderunCmds(1 [show ip arp vrf + each_vrf + summary])
arp_count += show_arp[0][dynamicEntries]return arp_count
def route_scale(node vrfs)route_count = 0for each_vrf in vrfs
show_route = noderunCmds(1 [show ip route vrf + each_vrf + summary])
route_count += show_route[0][totalRoutes]return route_count
def tcam_scale(node)tcam_count = 0show_tcam = noderunCmds(1 [
enable show platform trident tcam | include TCAM group] text)for each_line in show_tcam[1][output]splitlines()
tcam_count += int(each_linesplit()[4])return tcam_count
def hostname(node)host_name = noderunCmds(1 [show hostname])host_name_clean = str(host_name[0][hostname])return host_name_clean
def hardware_scale()form = eos_form()if formprocess()accepted
form_vars = formvarsswitches = switches_list(form_vars)
Hardware scale assessment scriptverify_scale =
for switch in switchestry
Define Connection Attributes for jsonrpcnode = Server(https + form_varsusername + +
form_varspassword + + switch +command-api)
Call the hardware scale functionsname = hostname(node)mac_count = mac_scale(node)vrfs = vrf_scale(node)arp_count = arp_scale(node vrfs)route_count = route_scale(node vrfs)tcam_count = tcam_scale(node)
112 Hardware Scale 205
arista-python-web2py Documentation Release 001
Store the values in a dictionary
verify_scale[name] = MAC Scale mac_countNumber of VRFs len(vrfs)Number of ARP Entries arp_countNumber of Routes route_countNumber of TCAM entries Used tcam_count
except ProtocolError as everify_scale[switch] = Invalid EOS Command + str(e)
exceptverify_scale[switch] = eAPI Connection Error
return dict(verify_scale=verify_scale)
return dict(form=form)
Test your script using the URL httpsltweb-servergtArista_EOS_Tooldefaulthardware_scale
Summary
We have ported all the five use cases to the tool You make a list of your common networking tasks and write thescripts using Python and then port it to the tool You can also keep all the network-related documentation in this toolLetrsquos go to the next chapter to learn about the web2py view
206 Chapter 11 Chapter 10 Web2Py - Capacity Planning Use Cases