Merged from Ken Cs flow_stats dev branch
diff --git a/README b/README
index 5d1b5c5..ea5b7a4 100644
--- a/README
+++ b/README
@@ -1,5 +1,6 @@
OpenFlow Testing Framework
July, 2010
+Last updated March 2012
Copyright (c) 2010 The Board of Trustees of The Leland Stanford
Junior University
@@ -30,7 +31,7 @@
You can check out OFTest with git with the following command:
- git clone git://openflow.org/oftest
+ git clone git://github.com/floodlight/oftest
Introduction
++++++++++++
@@ -62,15 +63,15 @@
Currently, switches must be running version 1.0 of OpenFlow.
- # git clone yuba:/usr/local/git/openflow-projects/oftest
+ # git clone git://github.com/floodlight/oftest
# cd oftest/tools/munger
# make install
# cd ../../tests
Make sure the switch you want to test is running --
see (4) below for the reference switch example.
# ./oft --list
- # sudo ./oft
- # sudo ./oft --verbose --log-file=""
+ # sudo ./oft --test-spec=Echo
+ # sudo ./oft --verbose --log-file=""
# sudo ./oft --test-spec=<mod> --platform=remote --host=...
Longer Start
@@ -81,8 +82,8 @@
* Root privilege on host running oft
* Switch running OpenFlow 1.0 and attempting to connect
to a controller on the machine running oft.
- * Python 2.5. You can run platforms using eth interfaces
- with Python 2.4.
+ * Python 2.5 or 2.6. You can run platforms using eth interfaces
+ with Python 2.4. Python 2.7 may work.
* Python setup tools (e.g.: sudo apt-get install python-setuptools)
* oftest checked out (called <oftest> here)
* scapy installed: http://www.secdev.org/projects/scapy/
@@ -139,6 +140,10 @@
4F. To clean up the virtual ethernet interfaces, use
sudo rmmod veth
+ New tools allow you to run an OVS instance as well. See
+ oftest/tools/ovs-ctl. You will need to install a version of
+ openvswitch. See http://openvswitch.org/.
+
5. Run oft
See Warning above; requires sudo to control the dataplane
cd <oftest>/tests
diff --git a/tests/basic.py b/tests/basic.py
index ac4d89f..220f7c8 100644
--- a/tests/basic.py
+++ b/tests/basic.py
@@ -209,36 +209,39 @@
do_barrier(self.controller)
for of_port in basic_port_map.keys():
- basic_logger.info("PKT IN test, port " + str(of_port))
- pkt = simple_tcp_packet()
- self.dataplane.send(of_port, str(pkt))
- #@todo Check for unexpected messages?
- count = 0
- while True:
- (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
- if not response: # Timeout
- break
- if str(pkt) == response.data: # Got match
- break
- if not basic_config["relax"]: # Only one attempt to match
- break
- count += 1
- if count > 10: # Too many tries
- break
+ for pkt, pt in [
+ (simple_tcp_packet(), "simple TCP packet"),
+ (simple_eth_packet(), "simple Ethernet packet"),
+ (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
- self.assertTrue(response is not None,
- 'Packet in message not received on port ' +
- str(of_port))
- if str(pkt) != response.data:
- basic_logger.debug("pkt len " + str(len(str(pkt))) +
- ": " + str(pkt))
- basic_logger.debug("resp len " +
- str(len(str(response.data))) +
- ": " + str(response.data))
+ basic_logger.info("PKT IN test with %s, port %s" % (pt, of_port))
+ self.dataplane.send(of_port, str(pkt))
+ #@todo Check for unexpected messages?
+ count = 0
+ while True:
+ (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
+ if not response: # Timeout
+ break
+ if str(pkt) == response.data[:len(str(pkt))]: # Got match
+ break
+ if not basic_config["relax"]: # Only one attempt to match
+ break
+ count += 1
+ if count > 10: # Too many tries
+ break
- self.assertEqual(str(pkt), response.data,
- 'Response packet does not match send packet' +
- ' for port ' + str(of_port))
+ self.assertTrue(response is not None,
+ 'Packet in message not received on port ' +
+ str(of_port))
+ if str(pkt) != response.data[:len(str(pkt))]:
+ basic_logger.debug("pkt len " + str(len(str(pkt))) +
+ ": " + str(pkt))
+ basic_logger.debug("resp len " +
+ str(len(str(response.data))) +
+ ": " + str(response.data))
+ self.assertTrue(False,
+ 'Response packet does not match send packet' +
+ ' for port ' + str(of_port))
class PacketOut(SimpleDataPlane):
"""
@@ -256,35 +259,40 @@
self.assertEqual(rc, 0, "Failed to delete all flows")
# These will get put into function
- outpkt = simple_tcp_packet()
of_ports = basic_port_map.keys()
of_ports.sort()
for dp_port in of_ports:
- msg = message.packet_out()
- msg.data = str(outpkt)
- act = action.action_output()
- act.port = dp_port
- self.assertTrue(msg.actions.add(act), 'Could not add action to msg')
+ for outpkt, opt in [
+ (simple_tcp_packet(), "simple TCP packet"),
+ (simple_eth_packet(), "simple Ethernet packet"),
+ (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
- basic_logger.info("PacketOut to: " + str(dp_port))
- rv = self.controller.message_send(msg)
- self.assertTrue(rv == 0, "Error sending out message")
+ basic_logger.info("PKT OUT test with %s, port %s" % (opt, dp_port))
+ msg = message.packet_out()
+ msg.data = str(outpkt)
+ act = action.action_output()
+ act.port = dp_port
+ self.assertTrue(msg.actions.add(act), 'Could not add action to msg')
- exp_pkt_arg = None
- exp_port = None
- if basic_config["relax"]:
- exp_pkt_arg = outpkt
- exp_port = dp_port
- (of_port, pkt, pkt_time) = self.dataplane.poll(timeout=1,
- port_number=exp_port,
- exp_pkt=exp_pkt_arg)
+ basic_logger.info("PacketOut to: " + str(dp_port))
+ rv = self.controller.message_send(msg)
+ self.assertTrue(rv == 0, "Error sending out message")
- self.assertTrue(pkt is not None, 'Packet not received')
- basic_logger.info("PacketOut: got pkt from " + str(of_port))
- if of_port is not None:
- self.assertEqual(of_port, dp_port, "Unexpected receive port")
- self.assertEqual(str(outpkt), str(pkt),
- 'Response packet does not match send packet')
+ exp_pkt_arg = None
+ exp_port = None
+ if basic_config["relax"]:
+ exp_pkt_arg = outpkt
+ exp_port = dp_port
+ (of_port, pkt, pkt_time) = self.dataplane.poll(timeout=1,
+ port_number=exp_port,
+ exp_pkt=exp_pkt_arg)
+
+ self.assertTrue(pkt is not None, 'Packet not received')
+ basic_logger.info("PacketOut: got pkt from " + str(of_port))
+ if of_port is not None:
+ self.assertEqual(of_port, dp_port, "Unexpected receive port")
+ self.assertEqual(str(outpkt), str(pkt)[:len(str(outpkt))],
+ 'Response packet does not match send packet')
class FlowStatsGet(SimpleProtocol):
"""
diff --git a/tests/flow_stats.py b/tests/flow_stats.py
index 473bda2..f7a8ad2 100644
--- a/tests/flow_stats.py
+++ b/tests/flow_stats.py
@@ -154,7 +154,8 @@
pa_logger.info("Sending packet to dp port " +
str(ingress_port))
self.dataplane.send(ingress_port, str(pkt))
- (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=test_timeout)
+ (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=
+ test_timeout)
self.assertTrue(rcv_pkt is not None, "Did not receive packet")
pa_logger.debug("Packet len " + str(len(pkt)) + " in on " +
str(rcv_port))
@@ -212,7 +213,8 @@
pa_logger.info("Sending packet to dp port " +
str(ingress_port))
self.dataplane.send(ingress_port, str(pkt))
- (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=test_timeout)
+ (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=
+ test_timeout)
self.assertTrue(rcv_pkt is not None, "Did not receive packet")
pa_logger.debug("Packet len " + str(len(pkt)) + " in on " +
str(rcv_port))
@@ -238,7 +240,8 @@
do_barrier(self.controller)
(response, raw) = self.controller.poll(ofp.OFPT_STATS_REPLY, 2)
- self.assertTrue(len(response.stats) >= 1, "Did not receive flow stats reply")
+ self.assertTrue(len(response.stats) >= 1,
+ "Did not receive flow stats reply")
total_packets = 0
for obj in response.stats:
# TODO: pad1 and pad2 fields may be nonzero, is this a bug?
@@ -343,7 +346,8 @@
pa_logger.info("Sending packet to dp port " +
str(ingress_port))
self.dataplane.send(ingress_port, str(pkt))
- (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=test_timeout)
+ (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=
+ test_timeout)
self.assertTrue(rcv_pkt is not None, "Did not receive packet")
pa_logger.debug("Packet len " + str(len(pkt)) + " in on " +
str(rcv_port))
@@ -370,7 +374,8 @@
do_barrier(self.controller)
(response, raw) = self.controller.poll(ofp.OFPT_STATS_REPLY, 2)
- self.assertTrue(len(response.stats) == 1, "Did not receive flow stats reply")
+ self.assertTrue(len(response.stats) == 1,
+ "Did not receive flow stats reply")
for obj in response.stats:
self.assertTrue(obj.flow_count == flow_count,
"Flow count " + str(obj.flow_count) +
diff --git a/tests/oft b/tests/oft
index 48816a1..c397372 100755
--- a/tests/oft
+++ b/tests/oft
@@ -221,8 +221,8 @@
parser.add_option("-p", "--port", dest="controller_port",
type="int", help="Port number of the test controller")
test_list_help = """Indicate tests to run. Valid entries are "all" (the
- default) or a comma separated list of:\n
- module Run all tests in the named module\n
+ default) or a comma separated list of:
+ module Run all tests in the named module
testcase Run tests in all modules with the name testcase
module.testcase Run the specific test case
"""
diff --git a/tests/pktact.py b/tests/pktact.py
index 86fc44e..9230afd 100644
--- a/tests/pktact.py
+++ b/tests/pktact.py
@@ -953,62 +953,104 @@
(pkt, exp_pkt, acts) = pkt_action_setup(self, mod_fields=['ip_tos'],
check_test_params=True)
flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt,
- action_list=acts, max_test=2)
+ action_list=acts, max_test=2, egr_count=-1)
class ModifyL2DstMC(BaseMatchCase):
"""
Modify the L2 dest and send to 2 ports
-
- Uses egr_count test parameter; defaults to 2
"""
def runTest(self):
sup_acts = supported_actions_get(self)
if not (sup_acts & 1 << ofp.OFPAT_SET_DL_DST):
- skip_message_emit(self, "ModifyL2dst test")
+ skip_message_emit(self, "ModifyL2dstMC test")
return
- egr_count = test_param_get(self.config, 'egr_count', default=2)
(pkt, exp_pkt, acts) = pkt_action_setup(self, mod_fields=['dl_dst'],
check_test_params=True)
flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt,
- action_list=acts, max_test=2, egr_count=egr_count)
+ action_list=acts, max_test=2, egr_count=-1)
+
+class ModifyL2DstIngress(BaseMatchCase):
+ """
+ Modify the L2 dest and send to the ingress port
+ """
+ def runTest(self):
+ sup_acts = supported_actions_get(self)
+ if not (sup_acts & 1 << ofp.OFPAT_SET_DL_DST):
+ skip_message_emit(self, "ModifyL2dstMC test")
+ return
+
+ (pkt, exp_pkt, acts) = pkt_action_setup(self, mod_fields=['dl_dst'],
+ check_test_params=True)
+ flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt,
+ action_list=acts, max_test=2, egr_count=0,
+ ing_port=True)
+
+class ModifyL2DstIngressMC(BaseMatchCase):
+ """
+ Modify the L2 dest and send to the ingress port
+ """
+ def runTest(self):
+ sup_acts = supported_actions_get(self)
+ if not (sup_acts & 1 << ofp.OFPAT_SET_DL_DST):
+ skip_message_emit(self, "ModifyL2dstMC test")
+ return
+
+ (pkt, exp_pkt, acts) = pkt_action_setup(self, mod_fields=['dl_dst'],
+ check_test_params=True)
+ flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt,
+ action_list=acts, max_test=2, egr_count=-1,
+ ing_port=True)
class ModifyL2SrcMC(BaseMatchCase):
"""
Modify the source MAC address (TP1) and send to multiple
-
- Uses egr_count test parameter; defaults to 2
"""
def runTest(self):
sup_acts = supported_actions_get(self)
if not (sup_acts & 1 << ofp.OFPAT_SET_DL_SRC):
- skip_message_emit(self, "ModifyL2Src test")
+ skip_message_emit(self, "ModifyL2SrcMC test")
return
- egr_count = test_param_get(self.config, 'egr_count', default=2)
(pkt, exp_pkt, acts) = pkt_action_setup(self, mod_fields=['dl_src'],
check_test_params=True)
flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt,
- action_list=acts, max_test=2, egr_count=egr_count)
+ action_list=acts, max_test=2, egr_count=-1)
class ModifyL2SrcDstMC(BaseMatchCase):
"""
Modify the L2 source and dest and send to 2 ports
-
- Uses egr_count test parameter; defaults to 2
"""
def runTest(self):
sup_acts = supported_actions_get(self)
- if not (sup_acts & 1 << ofp.OFPAT_SET_DL_DST):
- skip_message_emit(self, "ModifyL2dst test")
+ if (not (sup_acts & 1 << ofp.OFPAT_SET_DL_DST) or
+ not (sup_acts & 1 << ofp.OFPAT_SET_DL_SRC)):
+ skip_message_emit(self, "ModifyL2SrcDstMC test")
return
- egr_count = test_param_get(self.config, 'egr_count', default=2)
mod_fields = ['dl_dst', 'dl_src']
(pkt, exp_pkt, acts) = pkt_action_setup(self, mod_fields=mod_fields,
check_test_params=True)
flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt,
- action_list=acts, max_test=2, egr_count=egr_count)
+ action_list=acts, max_test=2, egr_count=-1)
+
+class ModifyL2DstVIDMC(BaseMatchCase):
+ """
+ Modify the L2 dest and send to 2 ports
+ """
+ def runTest(self):
+ sup_acts = supported_actions_get(self)
+ if (not (sup_acts & 1 << ofp.OFPAT_SET_DL_DST) or
+ not (sup_acts & 1 << ofp.OFPAT_SET_VLAN_VID)):
+ skip_message_emit(self, "ModifyL2DstVIDMC test")
+ return
+
+ mod_fields = ['dl_dst', 'dl_vlan']
+ (pkt, exp_pkt, acts) = pkt_action_setup(self,
+ start_field_vals={'dl_vlan_enable':True}, mod_fields=mod_fields,
+ check_test_params=True)
+ flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt,
+ action_list=acts, max_test=2, egr_count=-1)
#@todo Need to implement tagged versions of the above tests
diff --git a/tests/testutils.py b/tests/testutils.py
index 4c7420c..5a99a00 100644
--- a/tests/testutils.py
+++ b/tests/testutils.py
@@ -158,6 +158,16 @@
return pkt
+def simple_eth_packet(pktlen=60,
+ dl_dst='00:01:02:03:04:05',
+ dl_src='01:80:c2:00:00:00',
+ dl_type=0x88cc):
+ pkt = scapy.Ether(dst=dl_dst, src=dl_src, type=dl_type)
+
+ pkt = pkt/("0" * (pktlen - len(pkt)))
+
+ return pkt
+
def do_barrier(ctrl):
b = message.barrier_request()
ctrl.transact(b)
@@ -246,7 +256,7 @@
"Unexpected pkt on port " + str(ofport))
-def receive_pkt_verify(parent, egr_ports, exp_pkt):
+def receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port):
"""
Receive a packet and verify it matches an expected value
@param egr_port A single port or list of ports
@@ -264,14 +274,18 @@
# Expect a packet from each port on egr port list
for egr_port in egr_port_list:
+ check_port = egr_port
+ if egr_port == ofp.OFPP_IN_PORT:
+ check_port = ing_port
(rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(
- port_number=egr_port, timeout=1, exp_pkt=exp_pkt_arg)
+ port_number=check_port, timeout=1, exp_pkt=exp_pkt_arg)
if rcv_pkt is None:
- parent.logger.error("ERROR: No packet received from " + str(egr_port))
+ parent.logger.error("ERROR: No packet received from " +
+ str(check_port))
parent.assertTrue(rcv_pkt is not None,
- "Did not receive packet port " + str(egr_port))
+ "Did not receive packet port " + str(check_port))
parent.logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
str(rcv_port))
@@ -282,7 +296,7 @@
parent.logger.debug("Received len " + str(len(rcv_pkt)) + ": "
+ str(rcv_pkt).encode('hex'))
parent.assertEqual(str(exp_pkt), str(rcv_pkt),
- "Packet match error on port " + str(egr_port))
+ "Packet match error on port " + str(check_port))
def match_verify(parent, req_match, res_match):
"""
@@ -490,7 +504,7 @@
if exp_pkt is None:
exp_pkt = pkt
- receive_pkt_verify(parent, egr_ports, exp_pkt)
+ receive_pkt_verify(parent, egr_ports, exp_pkt, ing_port)
if check_expire:
#@todo Not all HW supports both pkt and byte counters
@@ -506,6 +520,9 @@
@returns An empty list if unable to find enough ports
"""
+ if how_many == 0:
+ return []
+
count = 0
egr_ports = []
for egr_idx in range(len(of_ports)):
@@ -519,7 +536,7 @@
def flow_match_test(parent, port_map, wildcards=0, dl_vlan=-1, pkt=None,
exp_pkt=None, action_list=None, check_expire=False,
- max_test=0, egr_count=1):
+ max_test=0, egr_count=1, ing_port=False):
"""
Run flow_match_test_port_pair on all port pairs
@@ -532,16 +549,22 @@
@param exp_pkt If not None, use this as the expected output pkt; els use pkt
@param action_list Additional actions to add to flow mod
@param check_expire Check for flow expiration message
+ @param egr_count Number of egress ports; -1 means get from config w/ dflt 2
"""
of_ports = port_map.keys()
of_ports.sort()
parent.assertTrue(len(of_ports) > 1, "Not enough ports for test")
test_count = 0
+ if egr_count == -1:
+ egr_count = test_param_get(parent.config, 'egr_count', default=2)
+
for ing_idx in range(len(of_ports)):
ingress_port = of_ports[ing_idx]
egr_ports = get_egr_list(parent, of_ports, egr_count,
exclude_list=[ingress_port])
+ if ing_port:
+ egr_ports.append(ofp.OFPP_IN_PORT)
if len(egr_ports) == 0:
parent.assertTrue(0, "Failed to generate egress port list")