210
arista-python-web2py Documentation Release 0.0.1 Anees Mohamed Sep 13, 2017

arista-python-web2py Documentation

  • Upload
    others

  • View
    46

  • Download
    0

Embed Size (px)

Citation preview

Page 1: arista-python-web2py Documentation

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

  • Preface
  • Chapter 1 Introducing Python Core Concepts
    • Python Implementation on Various Operating Systems
    • Python Package Manager (pip)
    • Installing Python Modules
    • Python Core Concepts
    • Summary
      • Chapter 2 Script Framework for Use Cases
        • First Script - Show version
        • Second Script - Running Show Version on Multiple Switches
        • Third Script - Using External Input
        • Fourth Script ndash Storing Result in a Dictionary
        • Fifth Script ndash Handling Exceptions
        • Summary ndash Script Framework
          • Chapter 3 Troubleshooting Use Cases
            • Monitor Data Plane Drops
            • Monitor Control Plane Drops
              • Chapter 4 Capacity Planning Use Cases
                • Port Capacity
                • Hardware Scalability Assessment
                  • Chapter 5 Web2Py Introduction
                    • Understanding the functionality of the Tool
                    • Web2Py Framework for the Tool
                      • Chapter 6 Web2Py Installation
                        • Install Required Packages
                        • Install Web2Py
                        • Start Web2Py Service
                          • Chapter 7 Creating a New Web2Py Application
                            • Default Admin Page
                            • Create a New Application
                            • Edit the Application
                              • Chapter 8 Web2Py - Prepare the Tool
                                • Add Switches
                                • View Switches
                                • Categorize Switches
                                • Summary
                                  • Chapter 9 Web2Py - Troubleshooting Use Cases
                                    • Data Plane Packet Drops
                                    • Control Plane Drops
                                    • Last 10 Sev 1-3 Log Messages
                                      • Chapter 10 Web2Py - Capacity Planning Use Cases
                                        • Port Capacity
                                        • Hardware Scale
                                        • Summary
Page 2: arista-python-web2py Documentation

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

  • Preface
  • Chapter 1 Introducing Python Core Concepts
    • Python Implementation on Various Operating Systems
    • Python Package Manager (pip)
    • Installing Python Modules
    • Python Core Concepts
    • Summary
      • Chapter 2 Script Framework for Use Cases
        • First Script - Show version
        • Second Script - Running Show Version on Multiple Switches
        • Third Script - Using External Input
        • Fourth Script ndash Storing Result in a Dictionary
        • Fifth Script ndash Handling Exceptions
        • Summary ndash Script Framework
          • Chapter 3 Troubleshooting Use Cases
            • Monitor Data Plane Drops
            • Monitor Control Plane Drops
              • Chapter 4 Capacity Planning Use Cases
                • Port Capacity
                • Hardware Scalability Assessment
                  • Chapter 5 Web2Py Introduction
                    • Understanding the functionality of the Tool
                    • Web2Py Framework for the Tool
                      • Chapter 6 Web2Py Installation
                        • Install Required Packages
                        • Install Web2Py
                        • Start Web2Py Service
                          • Chapter 7 Creating a New Web2Py Application
                            • Default Admin Page
                            • Create a New Application
                            • Edit the Application
                              • Chapter 8 Web2Py - Prepare the Tool
                                • Add Switches
                                • View Switches
                                • Categorize Switches
                                • Summary
                                  • Chapter 9 Web2Py - Troubleshooting Use Cases
                                    • Data Plane Packet Drops
                                    • Control Plane Drops
                                    • Last 10 Sev 1-3 Log Messages
                                      • Chapter 10 Web2Py - Capacity Planning Use Cases
                                        • Port Capacity
                                        • Hardware Scale
                                        • Summary
Page 3: arista-python-web2py Documentation

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

  • Preface
  • Chapter 1 Introducing Python Core Concepts
    • Python Implementation on Various Operating Systems
    • Python Package Manager (pip)
    • Installing Python Modules
    • Python Core Concepts
    • Summary
      • Chapter 2 Script Framework for Use Cases
        • First Script - Show version
        • Second Script - Running Show Version on Multiple Switches
        • Third Script - Using External Input
        • Fourth Script ndash Storing Result in a Dictionary
        • Fifth Script ndash Handling Exceptions
        • Summary ndash Script Framework
          • Chapter 3 Troubleshooting Use Cases
            • Monitor Data Plane Drops
            • Monitor Control Plane Drops
              • Chapter 4 Capacity Planning Use Cases
                • Port Capacity
                • Hardware Scalability Assessment
                  • Chapter 5 Web2Py Introduction
                    • Understanding the functionality of the Tool
                    • Web2Py Framework for the Tool
                      • Chapter 6 Web2Py Installation
                        • Install Required Packages
                        • Install Web2Py
                        • Start Web2Py Service
                          • Chapter 7 Creating a New Web2Py Application
                            • Default Admin Page
                            • Create a New Application
                            • Edit the Application
                              • Chapter 8 Web2Py - Prepare the Tool
                                • Add Switches
                                • View Switches
                                • Categorize Switches
                                • Summary
                                  • Chapter 9 Web2Py - Troubleshooting Use Cases
                                    • Data Plane Packet Drops
                                    • Control Plane Drops
                                    • Last 10 Sev 1-3 Log Messages
                                      • Chapter 10 Web2Py - Capacity Planning Use Cases
                                        • Port Capacity
                                        • Hardware Scale
                                        • Summary
Page 4: arista-python-web2py Documentation

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

  • Preface
  • Chapter 1 Introducing Python Core Concepts
    • Python Implementation on Various Operating Systems
    • Python Package Manager (pip)
    • Installing Python Modules
    • Python Core Concepts
    • Summary
      • Chapter 2 Script Framework for Use Cases
        • First Script - Show version
        • Second Script - Running Show Version on Multiple Switches
        • Third Script - Using External Input
        • Fourth Script ndash Storing Result in a Dictionary
        • Fifth Script ndash Handling Exceptions
        • Summary ndash Script Framework
          • Chapter 3 Troubleshooting Use Cases
            • Monitor Data Plane Drops
            • Monitor Control Plane Drops
              • Chapter 4 Capacity Planning Use Cases
                • Port Capacity
                • Hardware Scalability Assessment
                  • Chapter 5 Web2Py Introduction
                    • Understanding the functionality of the Tool
                    • Web2Py Framework for the Tool
                      • Chapter 6 Web2Py Installation
                        • Install Required Packages
                        • Install Web2Py
                        • Start Web2Py Service
                          • Chapter 7 Creating a New Web2Py Application
                            • Default Admin Page
                            • Create a New Application
                            • Edit the Application
                              • Chapter 8 Web2Py - Prepare the Tool
                                • Add Switches
                                • View Switches
                                • Categorize Switches
                                • Summary
                                  • Chapter 9 Web2Py - Troubleshooting Use Cases
                                    • Data Plane Packet Drops
                                    • Control Plane Drops
                                    • Last 10 Sev 1-3 Log Messages
                                      • Chapter 10 Web2Py - Capacity Planning Use Cases
                                        • Port Capacity
                                        • Hardware Scale
                                        • Summary
Page 5: arista-python-web2py Documentation

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

  • Preface
  • Chapter 1 Introducing Python Core Concepts
    • Python Implementation on Various Operating Systems
    • Python Package Manager (pip)
    • Installing Python Modules
    • Python Core Concepts
    • Summary
      • Chapter 2 Script Framework for Use Cases
        • First Script - Show version
        • Second Script - Running Show Version on Multiple Switches
        • Third Script - Using External Input
        • Fourth Script ndash Storing Result in a Dictionary
        • Fifth Script ndash Handling Exceptions
        • Summary ndash Script Framework
          • Chapter 3 Troubleshooting Use Cases
            • Monitor Data Plane Drops
            • Monitor Control Plane Drops
              • Chapter 4 Capacity Planning Use Cases
                • Port Capacity
                • Hardware Scalability Assessment
                  • Chapter 5 Web2Py Introduction
                    • Understanding the functionality of the Tool
                    • Web2Py Framework for the Tool
                      • Chapter 6 Web2Py Installation
                        • Install Required Packages
                        • Install Web2Py
                        • Start Web2Py Service
                          • Chapter 7 Creating a New Web2Py Application
                            • Default Admin Page
                            • Create a New Application
                            • Edit the Application
                              • Chapter 8 Web2Py - Prepare the Tool
                                • Add Switches
                                • View Switches
                                • Categorize Switches
                                • Summary
                                  • Chapter 9 Web2Py - Troubleshooting Use Cases
                                    • Data Plane Packet Drops
                                    • Control Plane Drops
                                    • Last 10 Sev 1-3 Log Messages
                                      • Chapter 10 Web2Py - Capacity Planning Use Cases
                                        • Port Capacity
                                        • Hardware Scale
                                        • Summary
Page 6: arista-python-web2py Documentation

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

  • Preface
  • Chapter 1 Introducing Python Core Concepts
    • Python Implementation on Various Operating Systems
    • Python Package Manager (pip)
    • Installing Python Modules
    • Python Core Concepts
    • Summary
      • Chapter 2 Script Framework for Use Cases
        • First Script - Show version
        • Second Script - Running Show Version on Multiple Switches
        • Third Script - Using External Input
        • Fourth Script ndash Storing Result in a Dictionary
        • Fifth Script ndash Handling Exceptions
        • Summary ndash Script Framework
          • Chapter 3 Troubleshooting Use Cases
            • Monitor Data Plane Drops
            • Monitor Control Plane Drops
              • Chapter 4 Capacity Planning Use Cases
                • Port Capacity
                • Hardware Scalability Assessment
                  • Chapter 5 Web2Py Introduction
                    • Understanding the functionality of the Tool
                    • Web2Py Framework for the Tool
                      • Chapter 6 Web2Py Installation
                        • Install Required Packages
                        • Install Web2Py
                        • Start Web2Py Service
                          • Chapter 7 Creating a New Web2Py Application
                            • Default Admin Page
                            • Create a New Application
                            • Edit the Application
                              • Chapter 8 Web2Py - Prepare the Tool
                                • Add Switches
                                • View Switches
                                • Categorize Switches
                                • Summary
                                  • Chapter 9 Web2Py - Troubleshooting Use Cases
                                    • Data Plane Packet Drops
                                    • Control Plane Drops
                                    • Last 10 Sev 1-3 Log Messages
                                      • Chapter 10 Web2Py - Capacity Planning Use Cases
                                        • Port Capacity
                                        • Hardware Scale
                                        • Summary
Page 7: arista-python-web2py Documentation

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

  • Preface
  • Chapter 1 Introducing Python Core Concepts
    • Python Implementation on Various Operating Systems
    • Python Package Manager (pip)
    • Installing Python Modules
    • Python Core Concepts
    • Summary
      • Chapter 2 Script Framework for Use Cases
        • First Script - Show version
        • Second Script - Running Show Version on Multiple Switches
        • Third Script - Using External Input
        • Fourth Script ndash Storing Result in a Dictionary
        • Fifth Script ndash Handling Exceptions
        • Summary ndash Script Framework
          • Chapter 3 Troubleshooting Use Cases
            • Monitor Data Plane Drops
            • Monitor Control Plane Drops
              • Chapter 4 Capacity Planning Use Cases
                • Port Capacity
                • Hardware Scalability Assessment
                  • Chapter 5 Web2Py Introduction
                    • Understanding the functionality of the Tool
                    • Web2Py Framework for the Tool
                      • Chapter 6 Web2Py Installation
                        • Install Required Packages
                        • Install Web2Py
                        • Start Web2Py Service
                          • Chapter 7 Creating a New Web2Py Application
                            • Default Admin Page
                            • Create a New Application
                            • Edit the Application
                              • Chapter 8 Web2Py - Prepare the Tool
                                • Add Switches
                                • View Switches
                                • Categorize Switches
                                • Summary
                                  • Chapter 9 Web2Py - Troubleshooting Use Cases
                                    • Data Plane Packet Drops
                                    • Control Plane Drops
                                    • Last 10 Sev 1-3 Log Messages
                                      • Chapter 10 Web2Py - Capacity Planning Use Cases
                                        • Port Capacity
                                        • Hardware Scale
                                        • Summary
Page 8: arista-python-web2py Documentation
Page 9: arista-python-web2py Documentation
Page 10: arista-python-web2py Documentation
Page 11: arista-python-web2py Documentation
Page 12: arista-python-web2py Documentation
Page 13: arista-python-web2py Documentation
Page 14: arista-python-web2py Documentation
Page 15: arista-python-web2py Documentation
Page 16: arista-python-web2py Documentation
Page 17: arista-python-web2py Documentation
Page 18: arista-python-web2py Documentation
Page 19: arista-python-web2py Documentation
Page 20: arista-python-web2py Documentation
Page 21: arista-python-web2py Documentation
Page 22: arista-python-web2py Documentation
Page 23: arista-python-web2py Documentation
Page 24: arista-python-web2py Documentation
Page 25: arista-python-web2py Documentation
Page 26: arista-python-web2py Documentation
Page 27: arista-python-web2py Documentation
Page 28: arista-python-web2py Documentation
Page 29: arista-python-web2py Documentation
Page 30: arista-python-web2py Documentation
Page 31: arista-python-web2py Documentation
Page 32: arista-python-web2py Documentation
Page 33: arista-python-web2py Documentation
Page 34: arista-python-web2py Documentation
Page 35: arista-python-web2py Documentation
Page 36: arista-python-web2py Documentation
Page 37: arista-python-web2py Documentation
Page 38: arista-python-web2py Documentation
Page 39: arista-python-web2py Documentation
Page 40: arista-python-web2py Documentation
Page 41: arista-python-web2py Documentation
Page 42: arista-python-web2py Documentation
Page 43: arista-python-web2py Documentation
Page 44: arista-python-web2py Documentation
Page 45: arista-python-web2py Documentation
Page 46: arista-python-web2py Documentation
Page 47: arista-python-web2py Documentation
Page 48: arista-python-web2py Documentation
Page 49: arista-python-web2py Documentation
Page 50: arista-python-web2py Documentation
Page 51: arista-python-web2py Documentation
Page 52: arista-python-web2py Documentation
Page 53: arista-python-web2py Documentation
Page 54: arista-python-web2py Documentation
Page 55: arista-python-web2py Documentation
Page 56: arista-python-web2py Documentation
Page 57: arista-python-web2py Documentation
Page 58: arista-python-web2py Documentation
Page 59: arista-python-web2py Documentation
Page 60: arista-python-web2py Documentation
Page 61: arista-python-web2py Documentation
Page 62: arista-python-web2py Documentation
Page 63: arista-python-web2py Documentation
Page 64: arista-python-web2py Documentation
Page 65: arista-python-web2py Documentation
Page 66: arista-python-web2py Documentation
Page 67: arista-python-web2py Documentation
Page 68: arista-python-web2py Documentation
Page 69: arista-python-web2py Documentation
Page 70: arista-python-web2py Documentation
Page 71: arista-python-web2py Documentation
Page 72: arista-python-web2py Documentation
Page 73: arista-python-web2py Documentation
Page 74: arista-python-web2py Documentation
Page 75: arista-python-web2py Documentation
Page 76: arista-python-web2py Documentation
Page 77: arista-python-web2py Documentation
Page 78: arista-python-web2py Documentation
Page 79: arista-python-web2py Documentation
Page 80: arista-python-web2py Documentation
Page 81: arista-python-web2py Documentation
Page 82: arista-python-web2py Documentation
Page 83: arista-python-web2py Documentation
Page 84: arista-python-web2py Documentation
Page 85: arista-python-web2py Documentation
Page 86: arista-python-web2py Documentation
Page 87: arista-python-web2py Documentation
Page 88: arista-python-web2py Documentation
Page 89: arista-python-web2py Documentation
Page 90: arista-python-web2py Documentation
Page 91: arista-python-web2py Documentation
Page 92: arista-python-web2py Documentation
Page 93: arista-python-web2py Documentation
Page 94: arista-python-web2py Documentation
Page 95: arista-python-web2py Documentation
Page 96: arista-python-web2py Documentation
Page 97: arista-python-web2py Documentation
Page 98: arista-python-web2py Documentation
Page 99: arista-python-web2py Documentation
Page 100: arista-python-web2py Documentation
Page 101: arista-python-web2py Documentation
Page 102: arista-python-web2py Documentation
Page 103: arista-python-web2py Documentation
Page 104: arista-python-web2py Documentation
Page 105: arista-python-web2py Documentation
Page 106: arista-python-web2py Documentation
Page 107: arista-python-web2py Documentation
Page 108: arista-python-web2py Documentation
Page 109: arista-python-web2py Documentation
Page 110: arista-python-web2py Documentation
Page 111: arista-python-web2py Documentation
Page 112: arista-python-web2py Documentation
Page 113: arista-python-web2py Documentation
Page 114: arista-python-web2py Documentation
Page 115: arista-python-web2py Documentation
Page 116: arista-python-web2py Documentation
Page 117: arista-python-web2py Documentation
Page 118: arista-python-web2py Documentation
Page 119: arista-python-web2py Documentation
Page 120: arista-python-web2py Documentation
Page 121: arista-python-web2py Documentation
Page 122: arista-python-web2py Documentation
Page 123: arista-python-web2py Documentation
Page 124: arista-python-web2py Documentation
Page 125: arista-python-web2py Documentation
Page 126: arista-python-web2py Documentation
Page 127: arista-python-web2py Documentation
Page 128: arista-python-web2py Documentation
Page 129: arista-python-web2py Documentation
Page 130: arista-python-web2py Documentation
Page 131: arista-python-web2py Documentation
Page 132: arista-python-web2py Documentation
Page 133: arista-python-web2py Documentation
Page 134: arista-python-web2py Documentation
Page 135: arista-python-web2py Documentation
Page 136: arista-python-web2py Documentation
Page 137: arista-python-web2py Documentation
Page 138: arista-python-web2py Documentation
Page 139: arista-python-web2py Documentation
Page 140: arista-python-web2py Documentation
Page 141: arista-python-web2py Documentation
Page 142: arista-python-web2py Documentation
Page 143: arista-python-web2py Documentation
Page 144: arista-python-web2py Documentation
Page 145: arista-python-web2py Documentation
Page 146: arista-python-web2py Documentation
Page 147: arista-python-web2py Documentation
Page 148: arista-python-web2py Documentation
Page 149: arista-python-web2py Documentation
Page 150: arista-python-web2py Documentation
Page 151: arista-python-web2py Documentation
Page 152: arista-python-web2py Documentation
Page 153: arista-python-web2py Documentation
Page 154: arista-python-web2py Documentation
Page 155: arista-python-web2py Documentation
Page 156: arista-python-web2py Documentation
Page 157: arista-python-web2py Documentation
Page 158: arista-python-web2py Documentation
Page 159: arista-python-web2py Documentation
Page 160: arista-python-web2py Documentation
Page 161: arista-python-web2py Documentation
Page 162: arista-python-web2py Documentation
Page 163: arista-python-web2py Documentation
Page 164: arista-python-web2py Documentation
Page 165: arista-python-web2py Documentation
Page 166: arista-python-web2py Documentation
Page 167: arista-python-web2py Documentation
Page 168: arista-python-web2py Documentation
Page 169: arista-python-web2py Documentation
Page 170: arista-python-web2py Documentation
Page 171: arista-python-web2py Documentation
Page 172: arista-python-web2py Documentation
Page 173: arista-python-web2py Documentation
Page 174: arista-python-web2py Documentation
Page 175: arista-python-web2py Documentation
Page 176: arista-python-web2py Documentation
Page 177: arista-python-web2py Documentation
Page 178: arista-python-web2py Documentation
Page 179: arista-python-web2py Documentation
Page 180: arista-python-web2py Documentation
Page 181: arista-python-web2py Documentation
Page 182: arista-python-web2py Documentation
Page 183: arista-python-web2py Documentation
Page 184: arista-python-web2py Documentation
Page 185: arista-python-web2py Documentation
Page 186: arista-python-web2py Documentation
Page 187: arista-python-web2py Documentation
Page 188: arista-python-web2py Documentation
Page 189: arista-python-web2py Documentation
Page 190: arista-python-web2py Documentation
Page 191: arista-python-web2py Documentation
Page 192: arista-python-web2py Documentation
Page 193: arista-python-web2py Documentation
Page 194: arista-python-web2py Documentation
Page 195: arista-python-web2py Documentation
Page 196: arista-python-web2py Documentation
Page 197: arista-python-web2py Documentation
Page 198: arista-python-web2py Documentation
Page 199: arista-python-web2py Documentation
Page 200: arista-python-web2py Documentation
Page 201: arista-python-web2py Documentation
Page 202: arista-python-web2py Documentation
Page 203: arista-python-web2py Documentation
Page 204: arista-python-web2py Documentation
Page 205: arista-python-web2py Documentation
Page 206: arista-python-web2py Documentation
Page 207: arista-python-web2py Documentation
Page 208: arista-python-web2py Documentation
Page 209: arista-python-web2py Documentation