The 2 minute video provides an overview of some of the performance challenges with leaf and spine fabrics and demonstrates Fabric View - a monitoring solution that leverages industry standard sFlow instrumentation in commodity data center switches to provide real-time visibility into fabric performance.
Fabric View is free to try, just register at http://www.myinmon.com/ and request an evaluation. The software requires an accurate network topology in order to characterize performance and this article will describe how to obtain the topology from a fabric of Arista Networks switches.
Arista EOS™ includes the eAPI JSON-RPC service for programmatic monitoring and control. The article Arista eAPI 101 introduces eAPI and describes how to enable the service in EOS. Enable eAPI on all the switches in the fabric.
Configure all the switches in the leaf and spine fabric to send sFlow to the Fabric View server. The following script demonstrates how sFlow can be configured programmatically using an eAPI script:
#!/usr/bin/env python import requests import json import signal from jsonrpclib import Server switch_list = ['switch1.example.com','switch2.example.com'] username = "admin" password = "password" sflow_collector = "192.168.56.1" sflow_port = "6343" sflow_polling = "20" sflow_sampling = "10000" for switch_name in switch_list: switch = Server("https://%s:%s@%s/command-api" % (username, password, switch_name)) response = switch.runCmds(1, ["enable", "configure", "sflow source %s" % switch_ip, "sflow destination %s %s" % (sflow_collector, sflow_port), "sflow polling-interval %s" % sflow_polling, "sflow sample output interface", "sflow sample dangerous %s" % sflow_sampling, "sflow run"])Next use the following eAPI script to discover the topology:
#/usr/bin/python ''' Copyright (c) 2015, Arista Networks, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Arista Networks nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARISTA NETWORKS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ''' # v0.5 - initial version of the script to discover network topology using # Arista eAPI and generate output in json format recognized by sFlow-RT. from jsonrpclib import Server import json from pprint import pprint # define switch in your topology, eapi transport protocol (http or https), # eapi username and password switch_list = ['switch1.example.com','switch2.example.com'] eapi_transport = 'https' eapi_username = 'admin' eapi_password = 'password' debug = False # internal variables used by the script allports = {} allswitches = {} allneighbors = [] alllinks = {} # method to populate allswitches and allports - called only from processNeighbor() def addPort(switchname, switchIP, portname, ifindex): id = switchname + '>' + portname prt = allports.setdefault(id, { "portname": portname, "linked": False }) if ifindex is not None: prt["ifindex"] = ifindex sw = allswitches.setdefault(switchname, { "name": switchname, "agent": switchIP, "ports": {} }); if switchIP is not None: sw["agent"] = switchIP sw["ports"][portname] = prt # method to collect neighbor records - called with each LLDP neighbor # entry as they are discovered def processNeighbor(localname,localip,localport,localifindex,remotename,remoteport): addPort(localname, localip, localport,localifindex); addPort(remotename, None, remoteport, None); allneighbors.append({ "localname": localname, "localport": localport, "remotename": remotename, "remoteport": remoteport }); # method to remove agents that we did not discover properly, or # that we did not intend to include in the topology. (If we # assigned an agent field to the switch then we assume it should stay.) def pruneAgents(): for nm,sw in allswitches.items(): #if not "agent" in sw: if sw['agent'] == '0.0.0.0' or not sw['agent']: del allswitches[nm] # method to test for a new link - called only from findLinks() def testLink(nbor,linkno): swname1 = nbor["localname"] swname2 = nbor["remotename"] # one of the switches might have been pruned out if swname1 not in allswitches or swname2 not in allswitches: return False sw1 = allswitches[swname1] sw2 = allswitches[swname2] pname1 = nbor["localport"] pname2 = nbor["remoteport"] port1 = sw1["ports"][pname1]; port2 = sw2["ports"][pname2]; if not port1["linked"] and not port2["linked"]: # add new link linkid = "link" + str(linkno) port1["linked"] = True; port2["linked"] = True; alllinks[linkid] = { "node1": nbor["localname"], "port1": nbor["localport"], "node2": nbor["remotename"], "port2": nbor["remoteport"] } return True return False # method to find unique links - call at the end once all the LLDP records have # been processed from all the switches def findLinks(): linkcount = 0 for nbor in allneighbors: if testLink(nbor, linkcount+1): linkcount += 1 # method to dump topology in json format recognized by sFlow-RT def dumpTopology(): topology = { "nodes": allswitches, "links": alllinks } print(json.dumps(topology, indent=4)) # method to get LLDP neighbors of each switch - calls processNeighbor() for each LLDP neighbor found def getLldpNeighbors(switch_name): try: switch = Server('%s://%s:%s@%s/command-api' % (eapi_transport, eapi_username, eapi_password, switch_name)) # Get LLDP neighbors commands = ["enable", "show lldp neighbors"] response = switch.runCmds(1, commands, 'json') neighbors = response[1]['lldpNeighbors'] # Get local hostname commands = ["enable", "show hostname"] response = switch.runCmds(1, commands, 'json') hostname = response[1]['hostname'] # Get SNMP ifIndexes commands = ["enable", "show snmp mib ifmib ifindex"] response = switch.runCmds(1, commands, 'json') interfaceIndexes = response[1]['ifIndex'] # Get sFlow agent source address commands = ["enable", "show sflow"] response = switch.runCmds(1, commands, 'json') sflowAddress = response[1]['ipv4Sources'][0]['ipv4Address'] # Create 2D array lldp_neighbors where each line has following entries #The script outputs a JSON representation of the topology, for example:, , , lldp_neighbors = [] for neighbor in neighbors: lldp_neighbors.append([neighbor['neighborDevice'].split('.')[0], neighbor['port'], neighbor['neighborPort'], interfaceIndexes[neighbor['port']]]) if (debug): pprint(lldp_neighbors) # collect switches, ports and neighbor-relationships for row in lldp_neighbors: processNeighbor(hostname, sflowAddress, row[1], # localport row[3], # localifindex row[0], # remotename row[2]) # remoteport # Print list of LLDP neighbors in human friendly format: # neighbor, , connected to local with remote if debug: print "Switch %s has following %d neighbors:" % (hostname[1], len(neighbors)) for i, neighbor in enumerate(lldp_neighbors): print "#%d neighbor, %s, connected to local %s with remote %s" % (i+1, neighbor[0], neighbor[1], neighbor[2]) except: print 'Exception while connecting to %s' % switch_name return [] for switch in switch_list: getLldpNeighbors(switch) pruneAgents() findLinks() dumpTopology()
{ "nodes": { "leaf332": { "name": "leaf332", "agent": "10.10.130.142", "ports": { "Management1": { "portname": "Management1", "ifindex": 999001, "linked": false }, "Ethernet50/1": { "portname": "Ethernet50/1", "ifindex": 50001, "linked": true }, "Ethernet36": { "portname": "Ethernet36", "ifindex": 36, "linked": true }, "Ethernet51/1": { "portname": "Ethernet51/1", "ifindex": 51001, "linked": true }, "Ethernet52/1": { "portname": "Ethernet52/1", "ifindex": 52001, "linked": true }, "Ethernet49/1": { "portname": "Ethernet49/1", "ifindex": 49001, "linked": true }, "Ethernet12": { "portname": "Ethernet12", "ifindex": 12, "linked": false }, "Ethernet35": { "portname": "Ethernet35", "ifindex": 35, "linked": true } } }, "leaf259": { "name": "leaf259", "agent": "10.10.129.220", "ports": { "Management1": { "portname": "Management1", "ifindex": 999001, "linked": false }, "Ethernet5/1": { "portname": "Ethernet5/1", "ifindex": 5001, "linked": true }, "Ethernet29": { "portname": "Ethernet29", "ifindex": 29, "linked": true }, "Ethernet32": { "portname": "Ethernet32", "ifindex": 32, "linked": true }, "Ethernet6/1": { "portname": "Ethernet6/1", "ifindex": 6001, "linked": true }, "Ethernet31": { "portname": "Ethernet31", "ifindex": 31, "linked": true }, "Ethernet30": { "portname": "Ethernet30", "ifindex": 30, "linked": true }, "Ethernet15/1": { "portname": "Ethernet15/1", "ifindex": 15001, "linked": false } } }, "leaf331": { "name": "leaf331", "agent": "10.10.130.141", "ports": { "Management1": { "portname": "Management1", "ifindex": 999001, "linked": false }, "Ethernet50/1": { "portname": "Ethernet50/1", "ifindex": 50001, "linked": true }, "Ethernet36": { "portname": "Ethernet36", "ifindex": 36, "linked": true }, "Ethernet1": { "portname": "Ethernet1", "ifindex": 1, "linked": false }, "Ethernet51/1": { "portname": "Ethernet51/1", "ifindex": 51001, "linked": true }, "Ethernet52/1": { "portname": "Ethernet52/1", "ifindex": 52001, "linked": true }, "Ethernet49/1": { "portname": "Ethernet49/1", "ifindex": 49001, "linked": true }, "Ethernet11": { "portname": "Ethernet11", "ifindex": 11, "linked": false }, "Ethernet35": { "portname": "Ethernet35", "ifindex": 35, "linked": true } } }, "leaf260": { "name": "leaf260", "agent": "10.10.129.221", "ports": { "Management1": { "portname": "Management1", "ifindex": 999001, "linked": false }, "Ethernet11/1": { "portname": "Ethernet11/1", "ifindex": 11001, "linked": false }, "Ethernet5/1": { "portname": "Ethernet5/1", "ifindex": 5001, "linked": true }, "Ethernet29": { "portname": "Ethernet29", "ifindex": 29, "linked": true }, "Ethernet32": { "portname": "Ethernet32", "ifindex": 32, "linked": true }, "Ethernet6/1": { "portname": "Ethernet6/1", "ifindex": 6001, "linked": true }, "Ethernet31": { "portname": "Ethernet31", "ifindex": 31, "linked": true }, "Ethernet30": { "portname": "Ethernet30", "ifindex": 30, "linked": true } } }, "core210": { "name": "core210", "agent": "10.10.129.185", "ports": { "Ethernet3/3/1": { "portname": "Ethernet3/3/1", "ifindex": 3037, "linked": false }, "Ethernet3/6/1": { "portname": "Ethernet3/6/1", "ifindex": 3073, "linked": true }, "Ethernet3/5/1": { "portname": "Ethernet3/5/1", "ifindex": 3061, "linked": true }, "Ethernet3/2/1": { "portname": "Ethernet3/2/1", "ifindex": 3025, "linked": false }, "Ethernet3/8/1": { "portname": "Ethernet3/8/1", "ifindex": 3097, "linked": true }, "Ethernet3/1/1": { "portname": "Ethernet3/1/1", "ifindex": 3013, "linked": false }, "Management1/1": { "portname": "Management1/1", "ifindex": 999011, "linked": false }, "Ethernet3/34/1": { "portname": "Ethernet3/34/1", "ifindex": 3409, "linked": false }, "Ethernet3/31/1": { "portname": "Ethernet3/31/1", "ifindex": 3373, "linked": false }, "Ethernet3/7/1": { "portname": "Ethernet3/7/1", "ifindex": 3085, "linked": true } } }, "core212": { "name": "core212", "agent": "10.10.129.64", "ports": { "Ethernet3/3/1": { "portname": "Ethernet3/3/1", "ifindex": 3037, "linked": false }, "Ethernet3/12/1": { "portname": "Ethernet3/12/1", "ifindex": 3145, "linked": false }, "Ethernet3/2/1": { "portname": "Ethernet3/2/1", "ifindex": 3025, "linked": false }, "Ethernet3/13/1": { "portname": "Ethernet3/13/1", "ifindex": 3157, "linked": false }, "Ethernet3/31/1": { "portname": "Ethernet3/31/1", "ifindex": 3373, "linked": false }, "Ethernet3/32/1": { "portname": "Ethernet3/32/1", "ifindex": 3385, "linked": false }, "Ethernet3/18/1": { "portname": "Ethernet3/18/1", "ifindex": 3217, "linked": true }, "Ethernet3/28/1": { "portname": "Ethernet3/28/1", "ifindex": 3337, "linked": true }, "Ethernet3/33/1": { "portname": "Ethernet3/33/1", "ifindex": 3397, "linked": false }, "Ethernet3/5/1": { "portname": "Ethernet3/5/1", "ifindex": 3061, "linked": true }, "Ethernet3/8/1": { "portname": "Ethernet3/8/1", "ifindex": 3097, "linked": true }, "Ethernet3/34/1": { "portname": "Ethernet3/34/1", "ifindex": 3409, "linked": false }, "Ethernet3/36/1": { "portname": "Ethernet3/36/1", "ifindex": 3433, "linked": false }, "Ethernet3/35/1": { "portname": "Ethernet3/35/1", "ifindex": 3421, "linked": false }, "Ethernet3/15/1": { "portname": "Ethernet3/15/1", "ifindex": 3181, "linked": true }, "Ethernet3/7/1": { "portname": "Ethernet3/7/1", "ifindex": 3085, "linked": true }, "Ethernet3/16/1": { "portname": "Ethernet3/16/1", "ifindex": 3193, "linked": true }, "Ethernet3/17/1": { "portname": "Ethernet3/17/1", "ifindex": 3205, "linked": true }, "Management1/1": { "portname": "Management1/1", "ifindex": 999011, "linked": false }, "Ethernet3/26/1": { "portname": "Ethernet3/26/1", "ifindex": 3313, "linked": true }, "Ethernet3/25/1": { "portname": "Ethernet3/25/1", "ifindex": 3301, "linked": true }, "Ethernet3/21/1": { "portname": "Ethernet3/21/1", "ifindex": 3253, "linked": false }, "Ethernet3/11/1": { "portname": "Ethernet3/11/1", "ifindex": 3133, "linked": false }, "Ethernet3/6/1": { "portname": "Ethernet3/6/1", "ifindex": 3073, "linked": true }, "Ethernet3/27/1": { "portname": "Ethernet3/27/1", "ifindex": 3325, "linked": true }, "Ethernet3/1/1": { "portname": "Ethernet3/1/1", "ifindex": 3013, "linked": false }, "Ethernet3/23/1": { "portname": "Ethernet3/23/1", "ifindex": 3277, "linked": false }, "Ethernet3/22/1": { "portname": "Ethernet3/22/1", "ifindex": 3265, "linked": false } } } }, "links": { "link5": { "node1": "leaf260", "node2": "core212", "port2": "Ethernet3/15/1", "port1": "Ethernet31" }, "link4": { "node1": "leaf260", "node2": "core212", "port2": "Ethernet3/5/1", "port1": "Ethernet30" }, "link7": { "node1": "leaf259", "node2": "core210", "port2": "Ethernet3/6/1", "port1": "Ethernet29" }, "link6": { "node1": "leaf260", "node2": "core212", "port2": "Ethernet3/25/1", "port1": "Ethernet32" }, "link1": { "node1": "leaf260", "node2": "leaf259", "port2": "Ethernet5/1", "port1": "Ethernet5/1" }, "link3": { "node1": "leaf260", "node2": "core210", "port2": "Ethernet3/5/1", "port1": "Ethernet29" }, "link2": { "node1": "leaf260", "node2": "leaf259", "port2": "Ethernet6/1", "port1": "Ethernet6/1" }, "link9": { "node1": "leaf259", "node2": "core212", "port2": "Ethernet3/16/1", "port1": "Ethernet31" }, "link8": { "node1": "leaf259", "node2": "core212", "port2": "Ethernet3/6/1", "port1": "Ethernet30" }, "link15": { "node1": "leaf331", "node2": "core212", "port2": "Ethernet3/17/1", "port1": "Ethernet51/1" }, "link14": { "node1": "leaf331", "node2": "core212", "port2": "Ethernet3/7/1", "port1": "Ethernet50/1" }, "link17": { "node1": "leaf332", "node2": "core210", "port2": "Ethernet3/8/1", "port1": "Ethernet49/1" }, "link16": { "node1": "leaf331", "node2": "core212", "port2": "Ethernet3/27/1", "port1": "Ethernet52/1" }, "link11": { "node1": "leaf331", "node2": "leaf332", "port2": "Ethernet35", "port1": "Ethernet35" }, "link10": { "node1": "leaf259", "node2": "core212", "port2": "Ethernet3/26/1", "port1": "Ethernet32" }, "link13": { "node1": "leaf331", "node2": "core210", "port2": "Ethernet3/7/1", "port1": "Ethernet49/1" }, "link12": { "node1": "leaf331", "node2": "leaf332", "port2": "Ethernet36", "port1": "Ethernet36" }, "link20": { "node1": "leaf332", "node2": "core212", "port2": "Ethernet3/28/1", "port1": "Ethernet52/1" }, "link19": { "node1": "leaf332", "node2": "core212", "port2": "Ethernet3/18/1", "port1": "Ethernet51/1" }, "link18": { "node1": "leaf332", "node2": "core212", "port2": "Ethernet3/8/1", "port1": "Ethernet50/1" } } }
Access the Fabric View web interface at http://fabricview:8008/ and navigate to the Settings tab:
Upload the JSON topology file by clicking on the disk icon in the Topology section. Alternatively, the topology can be installed programmatically using the Fabric View REST API documented at the bottom of the Settings page.
As soon as the topology is installed, traffic data should start appearing in Fabric View. The video provides a quick walkthrough of the software features.