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.

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

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:

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.