Monitoring table statistics is needed to evaluate a method whether it costs too much switch's resources or make a comparison between two methods. This post is shown how to get table statistics information such as active entries in switch.
Controller can query about datapath's current state using the OFPT_STATS_REQEST
message. The structure of OFPT_STATS_REQUEST
message is shown as following:
struct ofp_stats_request {
struct ofp_header header;
uint16_t type; /* One of the OFPST_* constants. */
uint16_t flags; /* OFPSF_REQ_* flags (none yet defined). */
uint8_t body[0]; /* Body of the request. */
};
OFP_ASSERT(sizeof(struct ofp_stats_request) == 12);
The switch responds with one or more OFPT_STATS_REPLY messages:
struct ofp_stats_reply {
struct ofp_header header;
uint16_t type; /* One of the OFPST_* constants. */
uint16_t flags; /* OFPSF_REPLY_* flags. */
uint8_t body[0]; /* Body of the reply. */
};
OFP_ASSERT(sizeof(struct ofp_stats_reply) == 12);
In both the request and response, the type field specifies the kind of information being passed and determines how the body field is interpreted:
enum ofp_stats_types {
/* Description of this OpenFlow switch.
* The request body is empty.
* The reply body is struct ofp_desc_stats. */
OFPST_DESC,
/* Individual flow statistics.
* The request body is struct ofp_flow_stats_request.
* The reply body is an array of struct ofp_flow_stats. */
OFPST_FLOW,
/* Aggregate flow statistics.
* The request body is struct ofp_aggregate_stats_request.
* The reply body is struct ofp_aggregate_stats_reply. */
OFPST_AGGREGATE,
/* Flow table statistics.
* The request body is empty.
* The reply body is an array of struct ofp_table_stats. */
OFPST_TABLE,
/* Physical port statistics.
* The request body is struct ofp_port_stats_request.
* The reply body is an array of struct ofp_port_stats. */
OFPST_PORT,
/* Queue statistics for a port
* The request body defines the port
* The reply body is an array of struct ofp_queue_stats */
OFPST_QUEUE,
/* Vendor extension.
* The request and reply bodies begin with a 32-bit vendor ID, which takes
* the same form as in "struct ofp_vendor_header". The request and reply
* bodies are otherwise vendor-defined. */
OFPST_VENDOR = 0xffff
};
Since we want to get the table statistics information from switch, we need to send OFPT_STATS_REQUEST
packet with type field OFPST_TABLE
.
The body of the reply consists of an array of the following:
/* Body of reply to OFPST_TABLE request. */
struct ofp_table_stats {
uint8_t table_id; /* Identifier of table. Lower numbered tables are consulted first. */
uint8_t pad[3]; /* Align to 32-bits. */
char name[OFP_MAX_TABLE_NAME_LEN];
uint32_t wildcards; /* Bitmap of OFPFW_* wildcards that are supported by the table. */
uint32_t max_entries; /* Max number of entries supported. */
uint32_t active_count; /* Number of active entries. */
uint64_t lookup_count; /* Number of packets looked up in table. */
uint64_t matched_count; /* Number of packets that hit table. */
};
OFP_ASSERT(sizeof(struct ofp_table_stats) == 64);
According to the body of reply message, following information about flow table in switch can be obtained.
Max number of entries supported
Number of active entries
Number of packets looked up in table
Number of packets that hit table
The following code is an example for monitoring active entries in switches based on the topology mentioned in Create a custom topology in Mininet (2).
from pox.core import core
import pox.openflow.libopenflow_01 as of
from pox.lib.revent import *
from pox.lib.recoco import Timer
from collections import defaultdict
from pox.openflow.discovery import Discovery
from pox.lib.util import dpid_to_str
import time
class tableStats(EventMixin):
def __init__(self,interval = 10):
self.tableActiveCount = {}
self.interval = interval
core.openflow.addListeners(self)
def _handle_ConnectionUp(self,event):
print "Switch %s has connected" %event.dpid
self.sendTableStatsRequest(event)
def _handle_TableStatsReceived(self,event):
sw = 's%s'%event.dpid
self.tableActiveCount[sw] = event.stats[0].active_count
print "TableStatsReceived"
print self.tableActiveCount
Timer(self.interval, self.sendTableStatsRequest,args=[event])
def sendTableStatsRequest(self, event):
sr = of.ofp_stats_request()
sr.type = of.OFPST_TABLE
event.connection.send(sr)
print "Send table stat message to Switch %s " %event.dpid
def launch(interval = '10'):
interval = int(interval)
core.registerNew(tableStats,interval)
The above codes send table statistic request once controller and switches get connected. And when controller receives TableStatsReceived
event, controller handles this event and sends table statistic request again in a interval (default is 10 seconds).
This program can be run by input
sudo ./pox/pox.py tableStat --interval=1
and the output is a dictionary like
{'s3': 0, 's2': 0, 's1': 0, 's6': 0, 's5': 0, 's4': 0}
References:
OpenFlow Switch Specification Version1.0.0
POX WIKI - Requesting statistics from switches