Topology
This tutorial describes how network topology can be used to extend the capabilities of sFlow-RT.
Configuring Topology
The diagram shows a simple leaf and spine topology annotated with the information needed to combine sFlow telemetry with the topological data.
The topology is defined by four links: link1, link2, link3, and link4. The links and their attributes are described in a JSON object:
{
"links": {
"link1": {
"node1": "leaf1",
"port1": "swp1",
"node2": "spine1",
"port2": "swp1"
},
"link2": {
"node1": "leaf1",
"port1": "swp2",
"node2": "spine2",
"port2": "swp1"
},
"link3": {
"node1": "leaf2",
"port1": "swp1",
"node2": "spine1",
"port2": "swp2"
},
"link4": {
"node1": "leaf2",
"port1": "swp2",
"node2": "spine2",
"port2": "swp2"
}
}
}
In order to link sFlow telemetry to the topology, sFlow-RT must be able to learn the mapping from node name and port name to sFlow agent address and ifIndex, e.g. node: leaf1 port: swp1 → agent: 192.168.1.1 ifIndex: 3
If the switches include the host_descr and port_name structures, see sFlow Structure Numbers, in their sFlow telemetry then the mapping will be learned automatically via sFlow. Switches using the Host sFlow agent include these structures.
Alternatively, SNMP can be used to discover this mapping by setting the following System Property:
snmp.ifname=yes
This setting instructs sFlow-RT to make an SNMP request to retrieve sysName and ifName for each agent and ifIndex discovered in the sFlow telemetry. By default SNMP version 2c will be used with the public SNMP communtiy string. Addition System Properties can be set to overide these defaults: snmp.version, snmp.community, snmp.user, snmp.authprotocol, snmp.authpasswd, snmp.privprotocol, and snmp.privpasswd
Finally, a nodes structure can be included in the topology to provide the mappings:
{
"nodes": {
"leaf1": {
"agent": "192.168.1.1",
"ports": {
"swp1": {
"ifindex": "3"
},
"swp2": {
"ifindex": "4"
}
}
},
"leaf2": {
"agent": "192.168.1.2",
"ports": {
"swp1": {
"ifindex": "3"
},
"swp2": {
"ifindex": "4"
}
}
},
"spine1": {
"agent": "192.168.1.3",
"ports": {
"swp1": {
"ifindex": "1"
},
"swp2": {
"ifindex": "2"
}
}
},
"spine2": {
"agent": "192.168.1.4",
"ports": {
"swp1": {
"ifindex": "1"
},
"swp2": {
"ifindex": "2"
}
}
}
},
"links": {
"link1": {
"node1": "leaf1",
"port1": "swp1",
"node2": "spine1",
"port2": "swp1"
},
"link2": {
"node1": "leaf1",
"port1": "swp2",
"node2": "spine2",
"port2": "swp1"
},
"link3": {
"node1": "leaf2",
"port1": "swp1",
"node2": "spine1",
"port2": "swp2"
},
"link4": {
"node1": "leaf2",
"port1": "swp2",
"node2": "spine2",
"port2": "swp2"
}
}
}
Additional information can be attached to node, link, and port objects in the form of tags. For example, attaching a type tag to nodes to identify their role in the topology.
{
"nodes": {
"leaf1": {
"tags": {
"type": "leaf",
...
Once the topology object has been contructed, it can be posted to sFlow-RT using the REST API:
curl -X PUT -H "Content-Type: application/json" -d @topology.json http://localhost:8008/topology/json
Alternatively, the topology object can be installed using the internal JavaScript API:
setTopology(topology);
See Writing Applications for more information on using sFlow-RT's REST and JavaScript APIs.

The sflow-rt/topology application persists topology across sFlow-RT restarts and provides dashboard to verify that all the nodes and links specified in the topology are being monitored.
Manually constructing the topology object is generally not feasible or recommended since it will likely contain errors. Ideally the network configuration and topology will be available in a centralized repository that can be queried to generate the information required by sFlow-RT. Alternatively, Link Layer Discovery Protocol (LLDP) data retrieved from network devices can be used to construct the topology.
Complex Topology and Wiring Validation in Data Centers describes the Prescriptive Topology Manager (PTM), a tool created by Cumulus Networks for validating wiring and enforcing compliance with the intended topology. The Python script below, ptm.py, converts the PTM dot file format to JSON and posts the result to sFlow-RT.
#!/usr/bin/env python
import sys, re, fileinput, requests
url = sys.argv[1]
topology = {'links':{}}
def dequote(s):
if (s[0] == s[-1]) and s.startswith(("'", '"')):
return s[1:-1]
return s
l = 1
for line in fileinput.input(sys.argv[2:]):
link = re.search('([\S]+):(\S+)\s*(--|->)\s*(\S+):([^\s;,]+)',line)
if link:
s1 = dequote(link.group(1))
p1 = dequote(link.group(2))
s2 = dequote(link.group(4))
p2 = dequote(link.group(5))
linkname = 'L%d' % (l)
l += 1
topology['links'][linkname] = {'node1':s1,'port1':p1,'node2':s2,'port2':p2}
requests.put(url,json=topology)
For example, the following command uploads topology.dot.
./ptm.py http://localhost:8008/topology/json topology.dot
The Python script below provides another example, combining a query to the Arista CloudVision server to find a list of switches and then using JSON RPC (eAPI) requests to query hostname, lldp, and ifindex information from each switch.
#!/usr/bin/env python
from jsonrpclib import Server
import json
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
import cvp
# CVP info
CVP_HOST = '127.0.0.1'
CVP_USER = 'cvpadmin'
CVP_PASSWD = 'arista'
# EAPI info
EAPI_TRANSPORT = 'http'
EAPI_USER = CVP_USER
EAPI_PASSWD = CVP_PASSWD
server = cvp.Cvp(CVP_HOST)
server.authenticate(CVP_USER, CVP_PASSWD)
nodes = {}
links = {}
topology = {'nodes':nodes, 'links':links}
linknames = {}
def getInfo(name,ip):
eapi = Server('%s://%s:%s@%s/command-api' % (EAPI_TRANSPORT, EAPI_USER, EAPI_PASSWD, ip))
commands = [
'enable',
'show lldp neighbors',
'show hostname',
'show snmp mib ifmib ifindex',
'show sflow'
]
response = eapi.runCmds(1, commands, 'json')
lldp = response[1]['lldpNeighbors']
hostname = response[2]['hostname']
ifIndexes = response[3]['ifIndex']
agentAddr = response[4]['ipv4Sources'][0]['ipv4Address']
dev = {}
nodes[hostname] = dev
dev['agent'] = agentAddr
ports = {}
dev['ports'] = ports
for p in ifIndexes:
ports[p] = {'ifindex':ifIndexes[p]}
for n in lldp:
if '%s %s' % (hostname,n['port']) < '%s %s' % (n['neighborDevice'],n['neighborPort']):
lname = '%s %s -> %s %s' % (hostname,n['port'],n['neighborDevice'],n['neighborPort'])
if linknames.get(lname) == None:
linknames[lname] = {'node1':hostname,'port1':n['port'],'node2':n['neighborDevice'],'port2':n['neighborPort']}
else:
lname = '%s %s -> %s %s' % (n['neighborDevice'],n['neighborPort'],hostname,n['port'])
if linknames.get(lname) == None:
linknames[lname] = {'node1':n['neighborDevice'],'port1':n['neighborPort'],'node2':hostname,'port2':n['port']}
def getInternalLinks():
linkno = 1
for n in linknames:
entry = linknames[n]
if nodes.get(entry['node1']) != None and nodes.get(entry['node2']) != None:
links['L%s' % linkno] = entry
linkno = linkno + 1
devicelist = server.getDevices()
for device in devicelist:
try:
getInfo(device.fqdn,device.ipAddress)
except:
print 'Exception while connecting to %s' % device.fqdn
getInternalLinks()
requests.put('http://127.0.0.1:8008/topology/json',json=topology)
Please share additional topology discovery scripts with the sFlow-RT community.
Using Topology
The following sFlow-RT applications are topology aware:
- Browse Metrics, select Agent: TOPOLOGY in user interface.
- Browse Flows, set System Properties,
browse-flows.agents=TOPOLOGY
andbrowse-flows.aggMode=edge
- Prometheus Exporter, set agent: TOPOLOGY and aggMode: edge when querying flows
- Fabric Metrics
- IXP Metrics
A number of analytics features are enabled once a topology has been installed.
Note: the examples in this section assume familiarity with Writing Applications.
The metric, table, dump, and activeflows queries understand the token TOPOLOGY in the agent field to mean the agents that are in the topology. For example, the following query identifies the port with the hightest ingress utilization:
curl http://localhost:8008/metric/TOPOLOGY/max:ifinutilization/json
In addition, the aggMode=edge option in an activeflows query uses the topology to combine flow measurements and provide an accurate measure of the total amount of traffic entering the topology, i.e. summed over the access ports.
curl "http://localhost:8008/activeflows/TOPOLOGY/tcp/json?aggMode=edge"
The node:, link: and ifname: key functions are enabled when a topology is installed, see Define Flows. For example, create a flow definition with the following flow keys:
node:inputifindex,ifname:inputifindex
The definition instructs sFlow-RT to lookup nodes and ports in the topology and generate records with node an port names::
spine1,swp1
leaf2,swp1
Note: Flow keys always imply a filter since all keys in the definition need to be present in order to generate a record. In this case, flows will only be generated for nodes, links and ports that are defined in the topology.
A number of JavaScript functions provide access to the topology:
setTopology()
topologyVersion()
topologyLinkNames()
topologyNodeNames()
topologyLink()
topologyNodePortNames()
topologyTags()
topologyNodeTag()
topologyPortTag()
topologyLinkTag()
topologyNodeLinks()
topologyNodePortNames()
topologyInterfaceToLink()
topologyPortToInterface()
topologyLinkToInterfaces()
topologyNodesForAgent()
topologyAgentForNode()
ifName()
topologyLinkMetric()
topologyLocatesHostMacs()
topologyLocateHostMac()
topologyLocateHostIP()
topologyLocateHostIP6()
topologyLocateHostAgent()
topologyLocateHostUUID()
topologyLocateHostUUID()
topologyDiameter()
topologyShortestPath()
topologyShortestPaths()
topologyMinimumSpanningTree()
The topologyLocate*
functions query an internal table that sFlow-RT maintains, learning
the MAC addresses from network traffic and the access port where they enter the topology.
Finally, Multi-tenant sFlow
describes how to use topology to de-duplicate and split telemetry streams by network tenant.
The /tenant/json
and /tenant/{name}/json
REST API and setTenant()
JavaScript functions support this feature.