Building an interface counter’s report on Arista (I): using the Python pyeapi library

Pyeapi is the Arista’s python library used to interact with their API. Leveraging it, we can pull structured data (JSON) directly from the switches.

This JSON format data comes as multiple dictionaries within a list, which means that we can very easily access to its corresponding keys and values to analyse the specific data we care about.

In this post, I am going to pull all interfaces’ CRC errors, input discards, and output discards from a switch. This information could be very valuable as a daily report to sanity check the health/congestion of any link within our infrastructure.

Under the hood, pyeapi talks to the Arista API using HTTPS POST messages, which means that without configuring the Arista API (by default is disabled) on the switches, this library will not work.

First of all, assuming the API has been enabled on the switch, we need to create a file in the home directory (cd ~) called .eapi.conf, this file is used by pyeapi to connect to the switch’s API.

It will look like the below:


[connection:switch1]
username: test
password: test
host: 10.1.1.1
transport: https

Next, using the pyeapi.connect_to() method, we can establish an HTTPS session between our control machine and our switch, so that we can start running normal “CLI syntax” commands and receive back the output in JSON, instead of a raw string.

node = pyeapi.connect_to('switch1')

This is an example of the command “show interfaces counters errors”:


pprint.pprint(node.enable('show interfaces counters errors'))
[{'command': 'show interfaces counters errors',
  'encoding': 'json',
  'result': {u'interfaceErrorCounters':
   {u'Ethernet1': {u'alignmentErrors': 0,
                  u'fcsErrors': 0,
                  u'frameTooLongs': 0,
                  u'frameTooShorts': 0,
                  u'inErrors': 0,
                  u'outErrors': 0,
                  u'symbolErrors': 0},
   u'Ethernet2': {u'alignmentErrors': 0,
                  u'fcsErrors': 0,
                  u'frameTooLongs': 0,
                  u'frameTooShorts': 0,
                  u'inErrors': 0,
                  u'outErrors': 0,
                  u'symbolErrors': 0},
   u'Ethernet3': {u'alignmentErrors': 0,
                  u'fcsErrors': 0,
                  u'frameTooLongs': 0,
                  u'frameTooShorts': 0,
                  u'inErrors': 0,
                  u'outErrors': 0,
                  u'symbolErrors': 0},
   u'Ethernet4': {u'alignmentErrors': 0,
                  u'fcsErrors': 0,
                  u'frameTooLongs': 0,
                  u'frameTooShorts': 0,
                  u'inErrors': 0,
                  u'outErrors': 0,
                  u'symbolErrors': 0},
   u'Ethernet5': {u'alignmentErrors': 0,
                  u'fcsErrors': 0,
                  u'frameTooLongs': 0,
                  u'frameTooShorts': 0,
                  u'inErrors': 0,
                  u'outErrors': 0,
                  u'symbolErrors': 0},
   u'Ethernet6': {u'alignmentErrors': 0,
                  u'fcsErrors': 0,
                  u'frameTooLongs': 0,
                  u'frameTooShorts': 0,
                  u'inErrors': 0,
                  u'outErrors': 0,
                  u'symbolErrors': 0},
   u'Ethernet7': {u'alignmentErrors': 0,
                  u'fcsErrors': 0,
                  u'frameTooLongs': 0,
                  u'frameTooShorts': 0,
                  u'inErrors': 0,
                  u'outErrors': 0,
                  u'symbolErrors': 0},
   u'Management1': {u'alignmentErrors': 0,
                    u'fcsErrors': 0,
                    u'frameTooLongs': 0,
                    u'frameTooShorts': 0,
                    u'inErrors': 0,
                    u'outErrors': 0,
                    u'symbolErrors': 0}}}}]

Note: The command needs to be issued with its full syntax, i.e. not using contractions like ‘sh int’ otherwise, it will not work.

As we can see, inside the outer list we have a dictionary with the key ‘result’ which has, within several nested dictionaries, the output of the command we issued.

Therefore, and this is the beauty of structured data, manipulating data will be as easy as extracting values from a dictionary.

For each of the interfaces on the switch, we have got multiple parameters. Since we just want to check CRC errors, we will look for every key (interface) and its ‘fcsErrors’ value.

Let’s try with interface Ethernet1 first:


>>> pprint.pprint(node.enable('show interfaces counters errors')[0]['result']['interfaceErrorCounters']['Ethernet1'])
{u'alignmentErrors': 0,
 u'fcsErrors': 0,
 u'frameTooLongs': 0,
 u'frameTooShorts': 0,
 u'inErrors': 0,
 u'outErrors': 0,
 u'symbolErrors': 0}

>>> pprint.pprint(node.enable('show interfaces counters errors')[0]['result']['interfaceErrorCounters']['Ethernet1']['fcsErrors'])
0

Now, in order to get the value for every single interface, we will need to iterate over the dictionary.


from __future__ import print_function
import pyeapi

node = pyeapi.connect_to('switch1')
crc_errors = (node.enable('show interfaces counters errors')[0]['result']['interfaceErrorCounters'])

for k,v in crc_errors.items():
    print('Interface {}: {} CRC errors'.format(k,v['fcsErrors']))

This is the output:


Interface Management1: 0 CRC errors
Interface Ethernet2: 0 CRC errors
Interface Ethernet3: 0 CRC errors
Interface Ethernet1: 0 CRC errors
Interface Ethernet6: 0 CRC errors
Interface Ethernet7: 0 CRC errors
Interface Ethernet4: 0 CRC errors
Interface Ethernet5: 0 CRC errors

If we want to just look for non-zero values, we can add this conditional:


for k,v in crc_errors.items():
    if v['fcsErrors'] != 0:
        print('Interface {}: {} CRC errors'.format(k,v['fcsErrors']))

The next thing we want to do is to get the input and output discards, once again let’s start with interface Ethernet1 first:


>>> pprint.pprint(node.enable('show interfaces counters discards')[0]['result']['interfaces']['Ethernet1'])
{u'inDiscards': 0, u'outDiscards': 512}

To get the values for every interface:


from __future__ import print_function
import pyeapi

node = pyeapi.connect_to('switch1')
discards = node.enable('show interfaces counters discards')[0]['result']['interfaces']

for k,v in discards.items():
    print('Interface {}: {} Output discards, {} Input discards').format(k,v['outDiscards'],v['inDiscards'])

Output:


Interface Management1: 0 Output discards, 0 Input discards
Interface Ethernet2: 0 Output discards, 0 Input discards
Interface Ethernet3: 0 Output discards, 0 Input discards
Interface Ethernet1: 512 Output discards, 0 Input discards
Interface Ethernet6: 0 Output discards, 0 Input discards
Interface Ethernet7: 0 Output discards, 0 Input discards
Interface Ethernet4: 0 Output discards, 0 Input discards
Interface Ethernet5: 0 Output discards, 0 Input discards

Non-zero values:


for k,v in discards.items():
     if v['outDiscards'] != 0:
         print('Interface {}: {} Output discards, {} Input discards').format(k,v['outDiscards'],v['inDiscards'])

Output:


Interface Ethernet1: 512 Output discards, 0 Input discards

You can find the full code in my Github repository here

One thought on “Building an interface counter’s report on Arista (I): using the Python pyeapi library

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s