Site Maintenance with Arista eAPI

I started a public GitHub Repository in order to share some of my Python automation tools.

I work remotely on a large number of Arista switches and I have developed a script that captures the state of the network in order to run a comparison before and after network maintenance events. We have a monitoring system in place that alerts on various SNMP traps and events, but often times I need to interpret a number of changes via my network engineer lens to confirm that a maintenance period has been completed successfully.

The first Python script I’m publishing interfaces with an Arista switch over HTTPS and captures various outputs via the eAPI: inventory with serial numbers, vlan states, mac address table, lldp neighbors, routing protocol states, and route entries to a text file. A diff on the files can be run post maintenance event to highlight any changes for review.

This type of granular record can show that an item such as a metric on a route has changed. Our monitoring system does not alert on this type of change as it is monitoring the BGP peer status.

 

Link github.com/brentnowak/arista-tools

Advertisements

Using Arista Telemetry to Monitor Network State

Arista’s Telemetry product allows you to stream network state in real-time from each piece of switching hardware to a central management point. Recently I’ve been waiting to prove out the visibility of the Telemetry product during a maintenance event. I recently had the opportunity when we were switching carriers on a transatlantic link between two sites.

The screenshot below shows a 30 minute slice of time where fiber optic links were brought down on Carrier A, traffic flow changed, connectivity was restored with Carrier B, and normal traffic flow resumed.

1 – Technicians disconnect fiber for the Primary Circuit at sites A and B.
2 – As the routing neighbor between the A and B sites drop, traffic is automatically moved to the Secondary Circuits.
3 – When optical connectivity has been restored between the two sites, the Primary Circuit re-establishes. Routing reconverges and traffic is shifted back onto the Primary Circuit.

Normal workflow for a change like this involves camping Syslog and the various switches involved, issuing commands to show network activity as the maintenance progressed. With Arista’s Telemetry product, I was able to see the state of various network components (light levels, interface state, bit rate, etc) all in a real-time display.

So far an impressive way to gain visibility into network state across a bunch of different metrics at a glance. I’m looking forward to test this product with my other future projects.

Force-Directed Network Diagram with Arista eAPI and D3.js

2016-05-23_force_direction_1Overview

The Arista eAPI give you the ability to interact with a switch over standard HTTPS and return structured JSON. Here a section of Python code to populate a database table to automatically generate a network diagram based on LLDP neighbor relationships.

 

Requirements

Arista EOS
Python 2.7
Postgresql
pyeapi

Database Tables

CREATE TABLE report.control
(
  id serial NOT NULL,
  "switchName" text NOT NULL,
  "interfaceName" text NOT NULL,
  "interfaceType" text,
  monitor boolean,
  description text NOT NULL,
  "remoteSwitchName" text,
  "remoteSwitchPort" text,
  "lineProtocolStatus" text,
  "interfaceStatus" text,
  site text,
  CONSTRAINT pk_report_control PRIMARY KEY ("switchName", "interfaceName")
)

Sample Python Code

Populating our database table with Switch and Interface information.

import pyeapi
pyeapi.load_config('nodes.conf')

def control_insert_lldp(switchName, interfaceName, remoteSwitchName, remoteSwitchPort):
    try:
        conn = psycopg2.connect(conn_string)
        cursor = conn.cursor()
        sql = '''
        UPDATE report.control
        SET "remoteSwitchName" = %s, "remoteSwitchPort" = %s
        WHERE "switchName" = %s AND "interfaceName" = %s
        '''
        data = (remoteSwitchName, remoteSwitchPort, switchName, interfaceName, )
        cursor.execute(sql, data, )
    except psycopg2.IntegrityError:
        conn.rollback()
    else:
        conn.commit()
    return 0

for switch in switches:
 node = pyeapi.connect_to(switch['deviceName'])
 try:
 output = node.enable('show lldp neighbors')
 neighbors = output[0]['result']['lldpNeighbors']
 for neighbor in neighbors:
 neighborDevice = removedomain(neighbor['neighborDevice'])
 control_insert_lldp(hostname, neighbor['port'], neighborDevice, neighbor['neighborPort'])
 except Exception as e:
 print(e)

Getting a list of switches from our database table.

def network_switches():
    conn = psycopg2.connect(conn_string)
    cursor = conn.cursor(cursor_factory=RealDictCursor)
    sql = '''
    SELECT
    DISTINCT(control."switchName") as "name",
      site."siteDescription" as group
    FROM
      report.control
    WHERE
      "remoteSwitchName" != ''
    ORDER BY
      control."switchName"
    '''
    cursor.execute(sql, )
    results = cursor.fetchall()
    return results

Returning a LLDP neighbor value if we have one for each switch interface.

def network_lldp_neighbors(switchName):
    conn = psycopg2.connect(conn_string)
    cursor = conn.cursor(cursor_factory=RealDictCursor)
    sql = '''
    SELECT
      DISTINCT(control."remoteSwitchName") as "remoteName"
    FROM
      report.control
    WHERE
      "switchName" = %s AND
      "remoteSwitchName" != ''
    ORDER BY "remoteSwitchName"
    '''
    data = (switchName, )
    cursor.execute(sql, data, )
    results = cursor.fetchall()
    return results

Create a JSON string for D3.js Force-Directed Graph.

def d3_lldp(element):
    links = []
    value = 1
    nodes = network_switches()
    idCount = 0
    for row in nodes:
        row['id'] = idCount
        # row['group'] = 1
        idCount += 1

    for node in nodes:
        lldpswitches = network_lldp_neighbors(node['name'])
        source = node['id']

        for connection in lldpswitches:
            for row in nodes:
                if row['name'] == connection['remoteName']:
                    target = row['id']
                    result = {'source': source, 'target': target, 'value': value}
                    links.append(result)

    result = {'nodes': nodes, 'links': links}
    return json.dumps(result)