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.

Advertisements

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)