Monday, March 23, 2015

OpenNetworking.tv interview


The OpenNetworking.tv interview includes a wide ranging discussion of current trends in the software defined networking (SDN), including: merchant silicon, analytics, probes, scaleability, Open vSwitch, network virtualization, VxLAN, network function virtualization (NFV),  Open Compute Project, white box / bare metal switches, leaf and spine topologies, large "Elephant" flow marking and steering, Cumulus Linux, Big Switch, orchestration, Puppet and Chef.

The interview and full transcript are available on SDxCentral: sFlow Creator Peter Phaal On Taming The Wilds Of SDN & Virtual Networking

Related articles on this blog include:

Friday, March 13, 2015

ECMP visibility with Cumulus Linux

Demo: Implementing the Big Data Design Guide in the Cumulus Workbench  is a great demonstration of the power of zero touch provisioning and automation. When the switches and servers boot they automatically pick up their operating systems and configurations for the complex Equal Cost Multi-Path (ECMP) routed network shown in the diagram.

Topology discovery with Cumulus Linux looked at an alternative Multi-Chassis Link Aggregation (MLAG) configuration and shows how to extract the configuration and monitor traffic on the network using sFlow and Fabric View.

The paper Hedera: Dynamic Flow Scheduling for Data Center Networks describes the impact of colliding flows on effective ECMP cross sectional bandwidth. The paper gives an example which demonstrates that effective cross sectional bandwidth can be reduced by a factor of between 20% to 60%, depending on the number of simultaneous flows per host.

This article uses the workbench to demonstrate the effect of large "Elephant" flow collisions on network throughput. The following script running on each of the servers uses the iperf tool to generate pairs of overlapping Elephant flows:
cumulus@server1:~$ while true; do iperf -c 10.4.2.2 -t 20; sleep 20; done
------------------------------------------------------------
Client connecting to 10.4.2.2, TCP port 5001
TCP window size: 1.06 MByte (default)
------------------------------------------------------------
[  3] local 10.4.1.2 port 57234 connected with 10.4.2.2 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-20.0 sec  21.9 GBytes  9.41 Gbits/sec
------------------------------------------------------------
Client connecting to 10.4.2.2, TCP port 5001
TCP window size: 1.06 MByte (default)
------------------------------------------------------------
[  3] local 10.4.1.2 port 57240 connected with 10.4.2.2 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-20.0 sec  10.1 GBytes  4.34 Gbits/sec
------------------------------------------------------------
Client connecting to 10.4.2.2, TCP port 5001
TCP window size: 1.06 MByte (default)
------------------------------------------------------------
[  3] local 10.4.1.2 port 57241 connected with 10.4.2.2 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-20.0 sec  21.9 GBytes  9.41 Gbits/sec
------------------------------------------------------------
The first iperf test achieves a TCP throughput of 9.41 Gbits/sec (the maximum achievable on the 10Gbit/s network in the workbench). However, the second test only achieves a throughput of 4.34 Gbits/sec. How can this result be explained?
The Top Flows table above confirms that two simultaneous elephant flows are being tracked by Fabric View.
The Traffic charts update every second and give a fine grained view of the traffic flows over time. The charts clearly show how iperf flows vary in throughput, with the low throughput runs achieving a throughput of approximately 50% of the network capacity (these results are consistent with 20% to 60% reported in the Hedera paper).
The Performance charts show what is happening. Packets take two hops as they are routed from leaf1 to leaf2 (via spine1 or spine2). Each iperf connection is able to fully utilize the two links to achieve line rate throughput. Comparing the Total Traffic and Busy Spine Links charts shows that peak total throughput of approximately 20Gbits/sec corresponds to interval when 4 spine links are busy. The throughput is halved during intervals when the routes overlap and share 1 or 2 links (shown in gold as Collisions on the Busy Spine Links chart).
Readers might be surprised by the frequency of collisions given the number of links in the network. Packets take two hops to go from leaf1 to leaf2 - routed via spine1 or spine2. In addition, the links between switches are paired, so there are 8 possible two hop paths from leaf1 to leaf2. The explanation involves looking at the conditional probability that the second flow with overlap with the first. Suppose the first flow takes is routed to spine1 via port swp1s0 and that spine1 routes the flow to leaf2 via port swp51. If the second flow is routed via any of the 4 paths through spine2, there is no collision. However, if it is routed via spine1, there is only 1 path that avoids collisions (leaf1 port swp1s1 to spine1 port swp52). This means that there is a 5 / 8 chance of avoiding a collision, or a 3/8 (37.5%) chance that the two flow will collide. The probability of flow collisions is surprisingly high even on very large networks with many spine switches and paths (see Birthday Paradox). 
Also note the Discards trend in the Congestion and Errors section. Comparing the rate of discards with Collisions in the Busy Spine Links chart shows that discards don't occur unless there are Elephant flow collisions on the busy links.
The Discard trend lags the Collision trend because discards are reported using sFlow counters and the Collision metric are based on packet samples - see Measurement delay, counters vs. packet samples
This example demonstrates the visibility into leaf and spine fabric performance achievable using standard sFlow instrumentation built into commodity switch hardware. If you have a leaf and spine network, request a free evaluation of Fabric View to better understand your network's performance.
This small four switch leaf and spine network is composed of 12 x 10 Gbits/sec links which would require 24 x 10 Gbits/sec taps with associated probes and collector to fully monitor using traditional tools used to monitor legacy data center networks. The cost and complexity of tapping leaf and spine topologies is prohibitive. However, leaf and spine switches typically include hardware support for the sFlow measurement standard, embedding line rate visibility into every switch port for network wide coverage at no extra cost. In this example, the Fabric View analytics software is running on a commodity physical or virtual server consuming 1% CPU and 200 MBytes RAM.
Real-time analytics for leaf and spine networks is a core enabling technology for software defined networking (SDN) control mechanisms that can automatically adapt the network to rapidly changing flow patterns and dramatically improve performance.
For example, REST API for Cumulus Linux ACLs describes how and SDN controller can remotely control switches. Use cases discussed on this blog include: Elephant flow marking,  Elephant flow steering, and DDoS mitigation.

Finally, Cumulus Linux runs on open switch hardware from Agema, Dell, Edge-Core, Penguin Computing, Quanta. In addition, Hewlett-Packard recently announced that they will soon be selling a new line of open network switches built by Accton Technologies and support Cumulus Linux. The increasing availability of low cost open networking hardware running Linux creates a platform for open source and commercial software developers to quickly build and deploy innovative solutions.

Wednesday, March 11, 2015

Topology discovery with Cumulus Linux

Demo: Implementing the OpenStack Design Guide in the Cumulus Workbench is a great demonstration of the power of zero touch provisioning and automation. When the switches and servers boot they automatically pick up their operating systems and configurations for the complex network shown in the diagram.
REST API for Cumulus Linux ACLs describes a REST server for remotely controlling ACLs on Cumulus Linux. This article will discuss recently added topology discovery methods that allow an SDN controller to learn topology and apply targeted controls (e.g Large "Elephant" flow marking, Large flow steering, DDoS mitigation, etc.).

Prescriptive Topology Manager

Complex Topology and Wiring Validation in Data Centers describes how Cumulus Networks' prescriptive topology manager (PTM) provides a simple method of verifying and enforcing correct wiring topologies.

The following REST call converts the topology from PTM's dot notation and returns a JSON representation:
cumulus@wbench:~$ curl http://leaf1:8080/ptm
Returns the result:
{
 "links": {
  "L1": {
   "node1": "leaf1", 
   "node2": "spine1", 
   "port1": "swp1s0", 
   "port2": "swp49"
  },
  ...
 }
}

LLDP

Prescriptive Topology Manager is preferred since it ensures that the discovered topology is correct. However, PTM builds on basic Link Level Discovery Protocol (LLDP), which provides an alternative method of topology discovery.

The following REST call return the hostname:
cumulus@wbench:~$ curl http://leaf1:8080/hostname
Returns result:
"leaf1"
The following REST call returns LLDP neighbor information:
cumulus@wbench:~$ curl http://leaf1:8080/lldp/neighbors

Returns result:
{
   "lldp": [
     {
       "interface": [
         {
           "name": "eth0",
           "via": "LLDP",
           "chassis": [
             {
               "id": [
                 {
                   "type": "mac",
                   "value": "6c:64:1a:00:2e:7f"
                 }
               ],
               "name": [
                 {
                   "value": "colo-tor-3"
                 }
               ]
             }
           ],
           "port": [
             {
               "id": [
                 {
                   "type": "ifname",
                   "value": "swp10"
                 }
               ],
               "descr": [
                 {
                   "value": "swp10"
                 }
               ]
             }
           ]
         },
         ...
     }
   ]
 }
The following REST call returns LLDP configuration information:
cumulus@wbench:~$ curl http://leaf1:8080/lldp/configuration
Returns result:
{
   "configuration": [
     {
       "config": [
         {
           "tx-delay": [
             {
               "value": "30"
             }
           ],
           ...
         }
       ]
     }
   ]
 }

Topology discovery with LLDP

The script lldp.py extracts LLDP data from all the switches in the network and compiles a topology:
#!/usr/bin/env python

import sys, re, fileinput, json, requests

switch_list = ['leaf1','leaf2','spine1','spine2']

l = 0
linkdb = {}
links = {}
for switch_name in switch_list:
  # verify that lldp configuration exports hostname,ifname information
  r = requests.get("http://%s:8080/lldp/configuration" % (switch_name));
  if r.status_code != 200: continue
  config = r.json()
  lldp_hostname = config['configuration'][0]['config'][0]['hostname'][0]['value']
  if lldp_hostname != '(none)': continue
  lldp_porttype = config['configuration'][0]['config'][0]['lldp_portid-type'][0]['value']
  if lldp_porttype != 'ifname': continue
  # local hostname 
  r = requests.get("http://%s:8080/hostname" % (switch_name));
  if r.status_code != 200: continue
  host = r.json()
  # get neighbors
  r = requests.get("http://%s:8080/lldp/neighbors" % (switch_name));
  if r.status_code != 200: continue
  neighbors = r.json()
  interfaces = neighbors['lldp'][0]['interface']
  for i in interfaces:
    # local port name
    port = i['name']
    # neighboring hostname
    nhost = i['chassis'][0]['name'][0]['value']
    # neighboring port name
    nport = i['port'][0]['descr'][0]['value']
    if not host or not port or not nhost or not nport: continue
    if host < nhost:
      link = {'node1':host,'port1':port,'node2':nhost,'port2':nport}
    else:
      link = {'node1':nhost,'port1':nport,'node2':host,'port2':port}
    keystr = "%s %s -- %s %s" % (link['node1'],link['port1'],link['node2'],link['port2'])
    if keystr in linkdb:
       # check consistency
       prev = linkdb[keystr]
       if (link['node1'] != prev['node1'] 
           or link['port1'] != prev['port1']
           or link['node2'] != prev['node2']
           or link['port2'] != prev['port2']): raise Exception('Mismatched LLDP', keystr)
    else:
       linkdb[keystr] = link
       linkname = 'L%d' % (l)
       links[linkname] = link
       l += 1

top = {'links':links}               
print json.dumps(top,sort_keys=True, indent=1)
Returns result:
cumulus@wbench:~$ ./lldp.py 
{
 "links": {
  "L0": {
   "node1": "colo-tor-3", 
   "node2": "leaf1", 
   "port1": "swp10", 
   "port2": "eth0"
  }, 
  ...
 }
}
The lldp.py script and the latest version of acl_server can be found on Github, https://github.com/pphaal/acl_server/

Demonstration

Fabric visibility with Cumulus Linux demonstrates the visibility into network performance provided by Cumulus Linux support for the sFlow standard (see Cumulus Networks, sFlow and data center automation). The screen shot shows 10Gbit/s Elephant flows traversing the network shown at the top of this article. The flows between server1 and server2 were generated using iperf tests running in a continuous loop.

The acl_server and sFlow agents are installed on the leaf1, leaf2, spine1, and spine2 switches. By default, the sFlow agents automatically pick up their settings using DNS Service Discovery (DNS-SD). Adding the following entry in the wbench DNS server zone file, /etc/bind/zones/lab.local.zone, enables sFlow on the switches and directs measurements to the wbench host:
_sflow._udp     30      SRV     0 0 6343 wbench
Note: For more information on running sFlow in the Cumulus workbench, see Demo: Monitoring Traffic on Cumulus Switches with sFlow). Another point to note, this workbench setup demonstrates the visibility into Link Aggregation (LAG) provides by sFlow (see Link aggregation).

Fabric View is installed on wbench and is configured with the network topology obtained from acl_server. The web interface is accessed through the workbench reverse proxy, but access is also possible using a VPN (see Setting up OpenVPN on the Cumulus Workbench).
This workbench example automatically provisions an OpenStack cluster on the two servers along with the network to connect them. In much the same way OpenStack provides access to virtual resources, Cumulus' Remote Lab leverages the automation capabilities of open hardware to provide multi-tenant access to physical servers and networks.
Finally, Cumulus Linux runs on open switch hardware from Agema, Dell, Edge-Core, Penguin Computing, Quanta. In addition, Hewlett-Packard recently announced that they will soon be selling a new line of open network switches built by Accton Technologies and support Cumulus Linux. This article, demonstrates the flexibility that open networking offers to developers and network administrators. If you are curious, its very easy to give Cumulus Linux a try.

Thursday, February 26, 2015

Broadcom ASIC table utilization metrics, DevOps, and SDN

Figure 1: Two-Level Folded CLOS Network Topology Example
Figure 1 from the Broadcom white paper, Engineered Elephant Flows for Boosting Application Performance in Large-Scale CLOS Networks, shows a data center leaf and spine topology. Leaf and spine networks are seeing rapid adoption since they provide the scaleability needed to cost effectively deliver the low latency, high bandwidth interconnect for cloud, big data, and high performance computing workloads.

Broadcom Trident ASICs are popular in white box, brite-box and branded data center switches from a wide range of vendors, including: Accton, Agema, Alcatel-Lucent, Arista, Cisco, Dell, Edge-Core, Extreme, Hewlett-Packard, IBM, Juniper, Penguin Computing, and Quanta.
Figure 2: OF-DPA Programming Pipeline for ECMP
Figure 2 shows the packet processing pipeline of a Broadcom ASIC. The pipeline consists of a number of linked hardware tables providing bridging, routing, access control list (ACL), and ECMP forwarding group functions. Operations teams need to be able to proactively monitor table utilizations in order to avoid performance problems associated with table exhaustion.

Broadcom's recently released sFlow specification, sFlow Broadcom Switch ASIC Table Utilization Structures, leverages the industry standard sFlow protocol to offer scaleable, multi-vendor, network wide visibility into the utilization of these hardware tables.

Support for the new extension has just been added to the open source Host sFlow agent, which runs on Cumulus Linux, a Debian based Linux distribution that supports open switch hardware from Agema, Dell, Edge-Core, Penguin Computing, Quanta. Hewlett-Packard recently announced that they will soon be selling a new line of open network switches built by Accton Technologies and supporting Cumulus Linux.
The speed with which this new features can be delivered on hardware from the wide range of vendors supporting Cumulus Linux is a powerful illustration of the power of open networking. While support for the Broadcom ASIC table extension has been checking into the Host sFlow trunk it hasn't yet made it into the Cumulus Networks binary repositories. However, Cumulus Linux is an open platform, so users are free to download sources, compile and install the latest software version direct from SourceForge.
The following output from the open source sflowtool command line utility shows the raw table measurements (this is in addition to the extensive set of sFlow measurements already exported via sFlow on Cumulus Linux):
bcm_asic_host_entries 4
bcm_host_entries_max 8192
bcm_ipv4_entries 0
bcm_ipv4_entries_max 0
bcm_ipv6_entries 0
bcm_ipv6_entries_max 0
bcm_ipv4_ipv6_entries 9
bcm_ipv4_ipv6_entries_max 16284
bcm_long_ipv6_entries 3
bcm_long_ipv6_entries_max 256
bcm_total_routes 10
bcm_total_routes_max 32768
bcm_ecmp_nexthops 0
bcm_ecmp_nexthops_max 2016
bcm_mac_entries 3
bcm_mac_entries_max 32768
bcm_ipv4_neighbors 4
bcm_ipv6_neighbors 0
bcm_ipv4_routes 0
bcm_ipv6_routes 0
bcm_acl_ingress_entries 842
bcm_acl_ingress_entries_max 4096
bcm_acl_ingress_counters 68
bcm_acl_ingress_counters_max 4096
bcm_acl_ingress_meters 18
bcm_acl_ingress_meters_max 8192
bcm_acl_ingress_slices 3
bcm_acl_ingress_slices_max 8
bcm_acl_egress_entries 36
bcm_acl_egress_entries_max 512
bcm_acl_egress_counters 36
bcm_acl_egress_counters_max 1024
bcm_acl_egress_meters 18
bcm_acl_egress_meters_max 512
bcm_acl_egress_slices 2
bcm_acl_egress_slices_max 2
The sflowtool output is useful for troubleshooting and is easy to parse with scripts.

DevOps


The diagram shows how the sFlow-RT analytics engine is used to deliver metrics and events to cloud based and on-site DevOps tools, see: Cloud analytics,  InfluxDB and GrafanaCloud AnalyticsMetric export to Graphite, and Exporting events using syslog.

For example, the following sFlow-RT application simplifies monitoring of the leaf and spine network by combining measurements from all the switches, identifying the switch with the maximum utilization of each table, pushing the summaries to operations dashboard every 15 seconds, and sending syslog events immediately when any table exceeds 80% utilization:
var network_wide_metrics = [
  'max:bcm_host_utilization',
  'max:bcm_mac_utilization',
  'max:bcm_ipv4_ipv6_utilization',
  'max:bcm_total_routes_utilization',
  'max:bcm_ecmp_nexthops_utilization',
  'max:bcm_acl_ingress_utilization',
  'max:bcm_acl_ingress_meters_utilization',
  'max:bcm_acl_ingress_counters_utilization',
  'max:bcm_acl_egress_utilization',
  'max:bcm_acl_egress_meters_utilization',
  'max:bcm_acl_egress_counters_utilization'
];

var max_utilization = 80;

setIntervalHandler(function() {
  var vals = metric('ALL',network_wide_metrics);
  var graphite_metrics = {};
  for each (var val in vals) {
    if(!val.hasOwnProperty('metricValue')) continue;

    // generate syslog events for over utilized tables
    if(val.metricValue >= max_utilization) {
       var event = {
         "asic_table":val.metricName,
         "utilization":val.metricValue,
         "switchIP":val.agent
       };
       try {
         syslog(
           '10.0.0.1', // syslog collector: splunk>, logstash, etc.
           514,        // syslog port
           16,         // facility = local0
           5,          // severity = notice
           event
        );
      } catch(e) { logWarning("syslog() failed " + e); }
    }

    // add metric to graphite set
    graphite_metrics["network.podA."+val.metricName] = val.metricValue;
  }

  // sent metrics to graphite
  try {
    graphite(
      '10.0.0.151',  // graphite server
      2003,          // graphite carbon UDP port
      graphite_metrics
    );
  } catch(e) { logWarning("graphite() failed " + e); }
},15);
The following screen capture shows the graphs starting to appear in Graphite:

Real-time traffic analytics


The table utilization metrics are only a part of the visibility that sFlow provides into the performance of a leaf and spine network.

A leaf and spine fabric is challenging to monitor. The fabric spreads traffic across all the switches and links in order to maximize bandwidth. Unlike traditional hierarchical network designs, where a small number of links can be monitored to provide visibility, a leaf and spine network has no special links or switches where running CLI commands or attaching a probe would provide visibility. Even if it were possible to attach probes, the effective bandwidth of a leaf and spine network can be as high as a Petabit/second, well beyond the capabilities of current generation monitoring tools.
Scaleable traffic measurement is possible because Broadcom ASICs implement hardware support for sFlow monitoring, providing cost effective, line rate visibility that is build into the switches and scales to all port speeds (1G, 10G, 25G, 40G, 50G, 100G, ...) and the high port counts found in large leaf and spine networks.
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 visibility with Cumulus Linux describes how to set up Fabric View to monitor a Cumulus Linux leaf and spine network.

SDN

Real-time network analytics are a fundamental driver for a number of important SDN use cases, allowing the SDN controller to rapidly detect changes in traffic and respond by applying active controls. SDN fabric controller for commodity data center switches describes how control of the ACL table is the key feature needed to to build scaleable SDN solutions.




REST API for Cumulus Linux ACLs describes open source software to allow an SDN controller to centrally manage the ACL tables on a large scale network of switches running Cumulus Linux.
The ability to install software on the switches is transformative, allowing third party developers and network operators transparent access to the full capabilities of the switch and build solutions that efficiently handle automation challenges.
A number of SDN use cases have been demonstrated that build on Cumulus Linux to leverage the real-time visibility and control capabilities of the switch ASIC:
Visit the sFlow.com web site to learn more about SDN control of leaf and spine networks.

Finally, the SDN use cases make extensive use of the ACL table and so this brings us full circle to the importance of the Broadcom sFlow extension providing visibility into the utilization of table resources.

Thursday, February 5, 2015

Cloud analytics

Librato is an example of a cloud based analytics service (now part of SolarWinds). Librato provides an easy to use REST API for pushing metrics into their cloud service. The web portal makes it simple to combine and trend data and build and share dashboards.

This article describes a proof of concept demonstrating how Librato's cloud service can be used to cost effectively monitor large scale cloud infrastructure by leveraging standard sFlow instrumentation. Librato offers a free 30 day trial, making it easy to evaluate solutions based on this demonstration.
The diagram shows the measurement pipeline. Standard sFlow measurements from hosts, hypervisors, virtual machines, containers, load balancers, web servers and network switches stream to the sFlow-RT real-time analytics engine. Metrics are pushed from sFlow-RT to Librato using the REST API.

Over 40 vendors implement the sFlow standard and compatible products are listed on sFlow.org. The open source Host sFlow agent exports standard sFlow metrics from hosts. For additional background, the Velocity conference talk provides an introduction to sFlow and case study from a large social networking site.


Librato's service is priced based on the number of data points that they need to store. For example, a Host sFlow agent reports approximately 50 measurements per node. Collecting all the measurements from a cluster of 100 servers would generate 5000 metrics and cost $1,000 per month if metrics are stored at 15 second intervals.
There are important scaleability and cost advantages to placing the sFlow-RT analytics engine in front of the metrics collection service. For example, in large scale cloud environments the metrics for each member of a dynamic pool isn't necessarily worth trending since virtual machines are frequently added and removed. Instead, sFlow-RT tracks all the members of the pool, calculates summary statistics for the pool, and logs the summary statistics. This pre-processing can significantly reduce storage requirements, reducing costs and increasing query performance. The sFlow-RT analytics software also calculates traffic flow metrics, hot/missed Memcache keys, top URLs, exports events via syslog to Splunk, Logstash etc. and provides access to detailed metrics through its REST API.
The following steps were involved in setting up the proof of concept.

First register for free trial at Librato.com.

Find or build a server with Java 1.7+ and install sFlow-RT:
wget http://www.inmon.com/products/sFlow-RT/sflow-rt.tar.gz
tar -xvzf sflow-rt.tar.gz
cd sflow-rt
Edit the init.js script and add the following lines (modifying the user and token from your Librato account):
var url = "https://metrics-api.librato.com/v1/metrics";
var user = "first.last@mycompany.com";
var token = "55add91c806fb5f634ad1a334789a32e8d10a597815e6865aa84f0749324450e";

setIntervalHandler(function() {
  var metrics = ['min:load_one','q1:load_one','med:load_one',
                 'q3:load_one','max:load_one'];
  var vals = metric('ALL',metrics,{os_name:['linux']});
  var gauges = {};
  for each (var val in vals) {
     gauges[val.metricName] = {
       "value": val.metricValue,
       "source": "Linux_Pool"
     };
  }
  var body = {"gauges":gauges};
  http(url,'post', 'application/json', JSON.stringify(body), user, token);
} , 15); 
Now start sFlow-RT:
./start.sh
Cluster performance metrics describes the summary metrics that sFlow-RT can calculate. In this case, the load average minimum, maximum, and quartiles for the cluster are being calculated and pushed to Librato every 15 seconds.

Install Host sFlow agents on the physical or virtual machines in your cluster and direct them to send metrics to the sFlow-RT host. The installation steps can be easily automated using orchestration tools like Puppet, Chef, Ansible, etc.

Physical and virtual switches in the cluster can be configured to send sFlow to sFlow-RT in order to add traffic metrics to the mix, exporting metrics that characterizing traffic between service tiers etc. However, in public cloud environments, traffic flow information is typically not available. The articles, Amazon Elastic Compute Cloud (EC2) and Rackspace cloudservers describe how Host sFlow agents can be configured to monitor traffic between virtual machines in the cloud.
Metrics should start appearing in Librato as soon as the Host sFlow agents are started.

In this example, sFlow-RT is exporting 5 metrics to summarize the cluster performance, reducing the total monthly cost of monitoring the cluster from $1,000 to $1. Of course there are likely to be more metrics that you will want to track, but the ability to selectively log high value metrics provides a way to control costs and maximize benefits.

Saturday, January 24, 2015

Fabric visibility with Arista EOS

A leaf and spine fabric is challenging to monitor. The fabric spreads traffic across all the switches and links in order to maximize bandwidth. Unlike traditional hierarchical network designs, where a small number of links can be monitored to provide visibility, a leaf and spine network has no special links or switches where running CLI commands or attaching a probe would provide visibility. Even if it were possible to attach probes, the effective bandwidth of a leaf and spine network can be as high as a Petabit/second, well beyond the capabilities of current generation monitoring tools.

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 
  # , , , 
  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()
The script outputs a JSON representation of the topology, for example:
{
    "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.

Tuesday, January 6, 2015

Open vSwitch performance monitoring

Credit: Accelerating Open vSwitch to “Ludicrous Speed”
Accelerating Open vSwitch to "Ludicrous Speed" describes the architecture of Open vSwitch. When a packet arrives, the OVS Kernel Module checks its cache to see if there is an entry that matches the packet. If there is a match then the packet is forwarded within the kernel. Otherwise, the packet is sent to the user space ovs-vswitchd process to determine the forwarding decision based on the set of OpenFlow rules that have been installed or, if no rules are found, by passing the packet to an OpenFlow controller. Once a forwarding decision has been made, the packet and the forwarding actions are passed back to the OVS Kernel Module which caches the decision and forwards the packet. Subsequent packets in the flow will then be matched by the cache and forwarded within the kernel.

The recent Open vSwitch 2014 Fall Conference included the talk, Managing Open vSwitch across a large heterogeneous fleet by Chad Norgan, describing Rackspace's experience with running a large scale OpenStack deployment using Open vSwitch for network virtualization. The talk describes the key metrics that Rackspace collects to monitor the performance of the large pools of Open vSwitch instances.

This article discusses the metrics presented in the Rackspace talk and describes how the embedded sFlow agent in Open vSwitch was extended to efficiently export the metrics.
The first chart trends the number of entries in each of the OVS Kernel Module caches across all the virtual switches in the OpenStack deployment.
The next chart trends the cache hit / miss rates for the OVS Kernel Module. Processing packets using cached entries in the kernel is much faster than sending the packet to user space and requires far fewer CPU cycles and so maintaining a high cache hit rate is critical to handling the large volume of traffic in a cloud data center.
The third chart from the Rackspace presentation tracks the CPU consumed by ovs-vswitchd as it handles cache misses. Excessive CPU utilization can result in poor network performance and dropped packets. Reducing the CPU cycles consumed by networking frees up resources that can be used to host additional virtual machines and generates additional revenue.

Currently, monitoring Open vSwitch cache performance involves polling each switch using the ovs-dpctl command and collecting the results. Polling is complex to configure and maintain and operational complexity is reduced if the Open vSwitch is able to push the metrics - see Push vs Pull

The following sFlow structure was defined to allow Open vSwitch to export cache statistics along with the other sFlow metrics that are pushed by the sFlow agent:
/* Open vSwitch data path statistics */
/* see datapath/datapath.h */
/* opaque = counter_data; enterprise = 0; format = 2207 */ 
struct ovs_dp_stats { 
  unsigned int hits;                                                
  unsigned int misses; 
  unsigned int lost;
  unsigned int mask_hits;
  unsigned int flows;
  unsigned int masks;
}
The sFlow agent was also extended to export CPU and memory statistics for the ovs-vswitchd process by populating the app_resources structure - see sFlow Application Structures.

These extensions are the latest in a set of recent enhancements to the Open vSwitch sFlow implementation, including:
The Open vSwitch project first added sFlow support five years ago and these recent enhancements build on the detailed visibility into network traffic provided by the core Open vSwitch sFlow implementation and the complementary visibility into hosts, hypervisors, virtual machines and containers provided by the Host sFlow project.
Visibility and the software defined data center
Broad support for the sFlow standard across the cloud data center stack provides simple, efficient, low cost, scaleable, and comprehensive visibility. The standard metrics can be consumed by a broad range of open source and commercial tools, including: sflowtool, sFlow-Trend, sFlow-RT, Ganglia, Graphite, InfluxDB and Grafana.