Merge branch 'master' of github.com:floodlight/oftest
diff --git a/src/python/oftest/dataplane.py b/src/python/oftest/dataplane.py
index 7ce2738..252726a 100644
--- a/src/python/oftest/dataplane.py
+++ b/src/python/oftest/dataplane.py
@@ -31,6 +31,19 @@
ETH_P_ALL = 0x03
RCV_TIMEOUT = 10000
+def match_exp_pkt(exp_pkt, pkt):
+ """
+ Compare the string value of pkt with the string value of exp_pkt,
+ and return True iff they are identical. If the length of exp_pkt is
+ less than the minimum Ethernet frame size (60 bytes), then padding
+ bytes in pkt are ignored.
+ """
+ e = str(exp_pkt)
+ p = str(pkt)
+ if len(e) < 60:
+ p = p[:len(e)]
+ return e == p
+
class DataPlanePort(Thread):
"""
Class defining a port monitoring object.
@@ -140,7 +153,7 @@
if (not self.parent.want_pkt_port or
self.parent.want_pkt_port == self.port_number):
if self.parent.exp_pkt:
- if str(self.parent.exp_pkt) != str(rcvmsg):
+ if not match_exp_pkt(self.parent.exp_pkt, rcvmsg):
drop_pkt = True
if not drop_pkt:
self.parent.got_pkt_port = self.port_number
@@ -331,7 +344,7 @@
pkt, time = self.port_list[port_number].dequeue(use_lock=False)
if not exp_pkt:
break
- if str(pkt) == str(exp_pkt):
+ if match_exp_pkt(exp_pkt, pkt):
break
pkt = None # Discard silently
if pkt:
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644
index 0000000..48da841
--- /dev/null
+++ b/tests/.gitignore
@@ -0,0 +1,3 @@
+*.pyc
+profiles/*.pyc
+*~
diff --git a/tests/basic.py b/tests/basic.py
index 9dec947..bf5798e 100644
--- a/tests/basic.py
+++ b/tests/basic.py
@@ -21,6 +21,7 @@
import logging
import unittest
+import random
import oftest.controller as controller
import oftest.cstruct as ofp
@@ -252,7 +253,7 @@
(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
+ if dataplane.match_exp_pkt(pkt, response.data): # Got match
break
if not basic_config["relax"]: # Only one attempt to match
break
@@ -263,12 +264,9 @@
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))
+ if not dataplane.match_exp_pkt(pkt, response.data):
+ basic_logger.debug("Sent %s" % format_packet(pkt))
+ basic_logger.debug("Resp %s" % format_packet(response.data))
self.assertTrue(False,
'Response packet does not match send packet' +
' for port ' + str(of_port))
@@ -321,9 +319,56 @@
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")
+ if not dataplane.match_exp_pkt(outpkt, pkt):
+ basic_logger.debug("Sent %s" % format_packet(outpkt))
+ basic_logger.debug("Resp %s" % format_packet(
+ str(pkt)[:len(str(outpkt))]))
self.assertEqual(str(outpkt), str(pkt)[:len(str(outpkt))],
'Response packet does not match send packet')
+class PacketOutMC(SimpleDataPlane):
+ """
+ Test packet out to multiple output ports
+
+ Send packet out message to controller for 1 to N dataplane ports and
+ verify the packet appears on the appropriate ports
+ """
+ def runTest(self):
+ # Construct packet to send to dataplane
+ # Send packet to dataplane
+ # Poll controller with expect message type packet in
+
+ rc = delete_all_flows(self.controller, basic_logger)
+ self.assertEqual(rc, 0, "Failed to delete all flows")
+
+ # These will get put into function
+ of_ports = basic_port_map.keys()
+ random.shuffle(of_ports)
+ for num_ports in range(1,len(of_ports)+1):
+ for outpkt, opt in [
+ (simple_tcp_packet(), "simple TCP packet"),
+ (simple_eth_packet(), "simple Ethernet packet"),
+ (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
+
+ dp_ports = of_ports[0:num_ports]
+ basic_logger.info("PKT OUT test with " + opt +
+ ", ports " + str(dp_ports))
+ msg = message.packet_out()
+ msg.data = str(outpkt)
+ act = action.action_output()
+ for i in range(0,num_ports):
+ act.port = dp_ports[i]
+ self.assertTrue(msg.actions.add(act),
+ 'Could not add action to msg')
+
+ basic_logger.info("PacketOut to: " + str(dp_ports))
+ rv = self.controller.message_send(msg)
+ self.assertTrue(rv == 0, "Error sending out message")
+
+ receive_pkt_check(self.dataplane, outpkt, dp_ports,
+ set(of_ports).difference(dp_ports),
+ self, basic_logger, basic_config)
+
class FlowStatsGet(SimpleProtocol):
"""
Get stats
@@ -367,6 +412,21 @@
self.assertTrue(response is not None, "Did not get response")
basic_logger.debug(response.show())
+class DescStatsGet(SimpleProtocol):
+ """
+ Get stats
+
+ Simply verify stats get transaction
+ """
+ def runTest(self):
+ basic_logger.info("Running DescStatsGet")
+
+ basic_logger.info("Sending stats request")
+ request = message.desc_stats_request()
+ response, pkt = self.controller.transact(request, timeout=2)
+ self.assertTrue(response is not None, "Did not get response")
+ basic_logger.debug(response.show())
+
class FlowMod(SimpleProtocol):
"""
Insert a flow
diff --git a/tests/caps.py b/tests/caps.py
index 9c2c697..bb85818 100644
--- a/tests/caps.py
+++ b/tests/caps.py
@@ -99,7 +99,7 @@
while True:
request.match.nw_src += 1
rv = obj.controller.message_send(request)
-# do_barrier(obj.controller)
+ do_barrier(obj.controller)
flow_count += 1
if flow_count % count_check == 0:
response, pkt = obj.controller.transact(tstats, timeout=2)
diff --git a/tests/flow_expire.py b/tests/flow_expire.py
index 55f8ebc..53d7181 100644
--- a/tests/flow_expire.py
+++ b/tests/flow_expire.py
@@ -22,11 +22,11 @@
#@var port_map Local copy of the configuration map from OF port
# numbers to OS interfaces
-pa_port_map = None
-#@var pa_logger Local logger object
-pa_logger = None
-#@var pa_config Local copy of global configuration data
-pa_config = None
+fe_port_map = None
+#@var fe_logger Local logger object
+fe_logger = None
+#@var fe_config Local copy of global configuration data
+fe_config = None
def test_set_init(config):
"""
@@ -37,14 +37,14 @@
basic.test_set_init(config)
- global pa_port_map
- global pa_logger
- global pa_config
+ global fe_port_map
+ global fe_logger
+ global fe_config
- pa_logger = logging.getLogger("pkt_act")
- pa_logger.info("Initializing test set")
- pa_port_map = config["port_map"]
- pa_config = config
+ fe_logger = logging.getLogger("flow_expire")
+ fe_logger.info("Initializing test set")
+ fe_port_map = config["port_map"]
+ fe_config = config
class FlowExpire(basic.SimpleDataPlane):
"""
@@ -55,13 +55,16 @@
Verify the flow expiration message is received
"""
def runTest(self):
- global pa_port_map
+ global fe_port_map
- of_ports = pa_port_map.keys()
+ # TODO: set from command-line parameter
+ test_timeout = 60
+
+ of_ports = fe_port_map.keys()
of_ports.sort()
self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
- rc = delete_all_flows(self.controller, pa_logger)
+ rc = delete_all_flows(self.controller, fe_logger)
self.assertEqual(rc, 0, "Failed to delete all flows")
pkt = simple_tcp_packet()
@@ -71,9 +74,13 @@
"Could not generate flow match from pkt")
act = action.action_output()
- ingress_port = pa_config["base_of_port"]
- egress_port = (pa_config["base_of_port"] + 1) % len(of_ports)
- pa_logger.info("Ingress " + str(ingress_port) +
+ of_ports = fe_port_map.keys()
+ of_ports.sort()
+ self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
+
+ ingress_port = of_ports[0]
+ egress_port = of_ports[1]
+ fe_logger.info("Ingress " + str(ingress_port) +
" to egress " + str(egress_port))
match.in_port = ingress_port
@@ -87,13 +94,14 @@
act.port = egress_port
self.assertTrue(request.actions.add(act), "Could not add action")
- pa_logger.info("Inserting flow")
+ fe_logger.info("Inserting flow")
rv = self.controller.message_send(request)
self.assertTrue(rv != -1, "Error installing flow mod")
do_barrier(self.controller)
- (response, raw) = self.controller.poll(ofp.OFPT_FLOW_REMOVED, 2)
-
+ (response, pkt) = self.controller.poll(exp_msg=ofp.OFPT_FLOW_REMOVED,
+ timeout=test_timeout)
+
self.assertTrue(response is not None,
'Did not receive flow removed message ')
diff --git a/tests/flow_stats.py b/tests/flow_stats.py
index 91c67a6..38560bc 100644
--- a/tests/flow_stats.py
+++ b/tests/flow_stats.py
@@ -49,6 +49,8 @@
@param config The configuration dictionary; see oft
"""
+ basic.test_set_init(config)
+
global fs_port_map
global fs_logger
global fs_config
@@ -58,6 +60,31 @@
fs_port_map = config["port_map"]
fs_config = config
+def sendPacket(obj, pkt, ingress_port, egress_port, test_timeout):
+
+ fs_logger.info("Sending packet to dp port " + str(ingress_port) +
+ ", expecting output on " + str(egress_port))
+ obj.dataplane.send(ingress_port, str(pkt))
+
+ exp_pkt_arg = None
+ exp_port = None
+ if fs_config["relax"]:
+ exp_pkt_arg = pkt
+ exp_port = egress_port
+
+ (rcv_port, rcv_pkt, pkt_time) = obj.dataplane.poll(timeout=1,
+ port_number=exp_port,
+ exp_pkt=exp_pkt_arg)
+ obj.assertTrue(rcv_pkt is not None,
+ "Packet not received on port " + str(egress_port))
+ fs_logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
+ str(rcv_port))
+ obj.assertEqual(rcv_port, egress_port,
+ "Packet received on port " + str(rcv_port) +
+ ", expected port " + str(egress_port))
+ obj.assertEqual(str(pkt), str(rcv_pkt),
+ 'Response packet does not match send packet')
+
class SingleFlowStats(basic.SimpleDataPlane):
"""
Verify flow stats are properly retrieved.
@@ -151,21 +178,8 @@
num_sends = random.randint(10,20)
fs_logger.info("Sending " + str(num_sends) + " test packets")
for i in range(0,num_sends):
- fs_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)
- self.assertTrue(rcv_pkt is not None, "Did not receive packet")
- fs_logger.debug("Packet len " + str(len(pkt)) + " in on " +
- str(rcv_port))
- self.assertEqual(rcv_port, egress_port, "Unexpected receive port")
- for j in range(0,test_timeout):
- if str(pkt) == str(rcv_pkt):
- break
- sleep(1)
- self.assertTrue(j < test_timeout,
- 'Timeout sending packets for flow stats test')
+ sendPacket(self, pkt, ingress_port, egress_port,
+ test_timeout)
self.verifyStats(match, ofp.OFPP_NONE, test_timeout, num_sends)
self.verifyStats(match, egress_port, test_timeout, num_sends)
@@ -209,22 +223,19 @@
return flow_mod_msg
- def sendPacket(self, pkt, ingress_port, egress_port, test_timeout):
- fs_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)
- self.assertTrue(rcv_pkt is not None, "Did not receive packet")
- fs_logger.debug("Packet len " + str(len(pkt)) + " in on " +
- str(rcv_port))
- self.assertEqual(rcv_port, egress_port, "Unexpected receive port")
- for j in range(0,test_timeout):
- if str(pkt) == str(rcv_pkt):
- break
- sleep(1)
- self.assertTrue(j < test_timeout,
- 'Timeout sending packets for flow stats test')
+ def sumStatsReplyCounts(self, response):
+ total_packets = 0
+ for obj in response.stats:
+ # TODO: pad1 and pad2 fields may be nonzero, is this a bug?
+ # for now, just clear them so the assert is simpler
+ #obj.match.pad1 = 0
+ #obj.match.pad2 = [0, 0]
+ #self.assertEqual(match, obj.match,
+ # "Matches do not match")
+ fs_logger.info("Received " + str(obj.packet_count)
+ + " packets")
+ total_packets += obj.packet_count
+ return total_packets
def verifyStats(self, match, out_port, test_timeout, packet_count):
stat_req = message.flow_stats_request()
@@ -235,29 +246,27 @@
all_packets_received = 0
for i in range(0,test_timeout):
fs_logger.info("Sending stats request")
+ # TODO: move REPLY_MORE handling to controller.transact?
response, pkt = self.controller.transact(stat_req,
timeout=test_timeout)
self.assertTrue(response is not None,
"No response to stats request")
- 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?
- # for now, just clear them so the assert is simpler
- #obj.match.pad1 = 0
- #obj.match.pad2 = [0, 0]
- #self.assertEqual(match, obj.match,
- # "Matches do not match")
- fs_logger.info("Received " + str(obj.packet_count) + " packets")
- total_packets += obj.packet_count
+ total_packets = self.sumStatsReplyCounts(response)
+
+ while response.flags == ofp.OFPSF_REPLY_MORE:
+ response, pkt = self.controller.poll(exp_msg=
+ ofp.OFPT_STATS_REPLY,
+ timeout=test_timeout)
+ total_packets += self.sumStatsReplyCounts(response)
+
if total_packets == packet_count:
all_packets_received = 1
break
sleep(1)
self.assertTrue(all_packets_received,
- "Packet count does not match number sent")
+ "Total stats packet count " + str(total_packets) +
+ " does not match number sent " + str(packet_count))
def runTest(self):
global fs_port_map
@@ -294,15 +303,18 @@
num_pkt2s = random.randint(10,30)
fs_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
for i in range(0,num_pkt1s):
- self.sendPacket(pkt1, ingress_port, egress_port1, test_timeout)
+ sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
for i in range(0,num_pkt2s):
- self.sendPacket(pkt2, ingress_port, egress_port2, test_timeout)
+ sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
match1 = parse.packet_to_flow_match(pkt1)
+ fs_logger.info("Verifying flow1's " + str(num_pkt1s) + " packets")
self.verifyStats(match1, ofp.OFPP_NONE, test_timeout, num_pkt1s)
match2 = parse.packet_to_flow_match(pkt2)
+ fs_logger.info("Verifying flow2's " + str(num_pkt2s) + " packets")
self.verifyStats(match2, ofp.OFPP_NONE, test_timeout, num_pkt2s)
match1.wildcards |= ofp.OFPFW_DL_SRC
+ fs_logger.info("Verifying combined " + str(num_pkt1s+num_pkt2s) + " packets")
self.verifyStats(match1, ofp.OFPP_NONE, test_timeout,
num_pkt1s+num_pkt2s)
# TODO: sweep through the wildcards to verify matching?
@@ -341,23 +353,6 @@
return flow_mod_msg
- def sendPacket(self, pkt, ingress_port, egress_port, test_timeout):
- fs_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)
- self.assertTrue(rcv_pkt is not None, "Did not receive packet")
- fs_logger.debug("Packet len " + str(len(pkt)) + " in on " +
- str(rcv_port))
- self.assertEqual(rcv_port, egress_port, "Unexpected receive port")
- for j in range(0,test_timeout):
- if str(pkt) == str(rcv_pkt):
- break
- sleep(1)
- self.assertTrue(j < test_timeout,
- 'Timeout sending packets for flow stats test')
-
def verifyAggFlowStats(self, match, out_port, test_timeout,
flow_count, packet_count):
stat_req = message.aggregate_stats_request()
@@ -424,9 +419,9 @@
num_pkt2s = random.randint(10,30)
fs_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
for i in range(0,num_pkt1s):
- self.sendPacket(pkt1, ingress_port, egress_port1, test_timeout)
+ sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
for i in range(0,num_pkt2s):
- self.sendPacket(pkt2, ingress_port, egress_port2, test_timeout)
+ sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
# loop on flow stats request until timeout
match = parse.packet_to_flow_match(pkt1)
diff --git a/tests/oft b/tests/oft
index c397372..828eefa 100755
--- a/tests/oft
+++ b/tests/oft
@@ -134,6 +134,9 @@
except:
sys.exit("Need to install scapy for packet parsing")
+##@var Profile module
+profile_mod = None
+
##@var DEBUG_LEVELS
# Map from strings to debugging levels
DEBUG_LEVELS = {
@@ -167,11 +170,13 @@
"debug" : _debug_default,
"dbg_level" : _debug_level_default,
"port_map" : {},
- "test_params" : "None"
+ "test_params" : "None",
+ "profile" : None
}
# Default test priority
TEST_PRIO_DEFAULT=100
+TEST_PRIO_SKIP=-1
#@todo Set up a dict of config params so easier to manage:
# <param> <cmdline flags> <default value> <help> <optional parser>
@@ -245,6 +250,8 @@
help="Relax packet match checks allowing other packets")
parser.add_option("--param", type="int",
help="Parameter sent to test (for debugging)")
+ parser.add_option("--profile",
+ help="File listing tests to skip/run")
parser.add_option("-t", "--test-params",
help="""Set test parameters: key=val;...
NOTE: key MUST be a valid Python identifier, egr_count not egr-count
@@ -257,6 +264,29 @@
return (config, args)
+def check_profile(config):
+ """
+ Import a profile from the profiles library
+ """
+
+ global profile_mod
+ if "profile" in config and config["profile"]:
+ logging.info("Importing profile: %s" % config["profile"])
+ profile_name = "profiles." + config["profile"]
+ try:
+ top_mod = __import__(profile_name)
+ profile_mod = eval("top_mod." + config["profile"])
+ logging.info("Imported profile %s. Dir: %s" %
+ (config["profile"], str(dir(profile_mod))))
+ except:
+ logging.info("Could not import profile: %s.py" %
+ config["profile"])
+ print "Failed to import profile: %s" % config["profile"]
+ profile_mod = None
+ else:
+ logging.info("No profile specified")
+
+
def logging_setup(config):
"""
Set up logging based on config
@@ -359,9 +389,16 @@
def test_prio_get(mod, test):
"""
Return the priority of a test
+
+ If test is in "skip list" from profile, return the skip value
+
If set in the test_prio variable for the module, return
that value. Otherwise return 100 (default)
"""
+ if profile_mod:
+ if profile_mod.skip_test_list and test in profile_mod.skip_test_list:
+ logging.info("Skipping test %s due to profile" % test)
+ return TEST_PRIO_SKIP
if 'test_prio' in dir(mod):
if test in mod.test_prio.keys():
return mod.test_prio[test]
@@ -410,11 +447,14 @@
print "Tests preceded by * are not run by default"
print "Tests marked (TP1) after name take --test-params including:"
print " 'vid=N;strip_vlan=bool;add_vlan=bool'"
+ print "Note that --profile may override which tests are run"
sys.exit(0)
logging_setup(config)
logging.info("++++++++ " + time.asctime() + " ++++++++")
+check_profile(config)
+
# Generate the test suite
#@todo Decide if multiple suites are ever needed
suite = unittest.TestSuite()
diff --git a/tests/pktact.py b/tests/pktact.py
index 129f96d..693293b 100644
--- a/tests/pktact.py
+++ b/tests/pktact.py
@@ -80,6 +80,8 @@
@param config The configuration dictionary; see oft
"""
+ basic.test_set_init(config)
+
global pa_port_map
global pa_logger
global pa_config
@@ -800,9 +802,11 @@
Verify flow_expiration message is correct when command option is set
"""
def runTest(self):
+ vid = test_param_get(self.config, 'vid', default=TEST_VID_DEFAULT)
for wc in WILDCARD_VALUES:
if wc & ofp.OFPFW_DL_VLAN:
- dl_vlan = 0
+ # Set nonzero VLAN id to avoid sending priority-tagged packet
+ dl_vlan = vid
else:
dl_vlan = -1
flow_match_test(self, pa_port_map, wildcards=wc,
@@ -830,10 +834,12 @@
Verify flow_expiration message is correct when command option is set
"""
def runTest(self):
+ vid = test_param_get(self.config, 'vid', default=TEST_VID_DEFAULT)
for wc in WILDCARD_VALUES:
all_exp_one_wildcard = ofp.OFPFW_ALL ^ wc
if all_exp_one_wildcard & ofp.OFPFW_DL_VLAN:
- dl_vlan = 0
+ # Set nonzero VLAN id to avoid sending priority-tagged packet
+ dl_vlan = vid
else:
dl_vlan = -1
flow_match_test(self, pa_port_map, wildcards=all_exp_one_wildcard,
@@ -1111,7 +1117,7 @@
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")
+ skip_message_emit(self, "ModifyL2dstIngress test")
return
(pkt, exp_pkt, acts) = pkt_action_setup(self, mod_fields=['dl_dst'],
@@ -1186,13 +1192,92 @@
flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt,
action_list=acts, max_test=2, egr_count=-1)
+class FlowToggle(BaseMatchCase):
+ """
+ Add flows to the table and modify them repeatedly
+ """
+ def runTest(self):
+ flow_count = test_param_get(self.config, 'ft_flow_count', default=20)
+ iter_count = test_param_get(self.config, 'ft_iter_count', default=10)
+
+ pa_logger.info("Running flow toggle with %d flows, %d iterations" %
+ (flow_count, iter_count))
+ acts = []
+ acts.append(action.action_output())
+ acts.append(action.action_output())
+
+ of_ports = pa_port_map.keys()
+ if len(of_ports) < 3:
+ self.assertTrue(False, "Too few ports for test")
+
+ for idx in range(2):
+ acts[idx].port = of_ports[idx]
+
+ flows = []
+ flows.append([])
+ flows.append([])
+
+ wildcards = ofp.OFPFW_DL_SRC | ofp.OFPFW_DL_DST
+ # Create up the flows in an array
+ for toggle in range(2):
+ for f_idx in range(flow_count):
+ pkt = simple_tcp_packet(tcp_sport=f_idx)
+ msg = message.flow_mod()
+ match = parse.packet_to_flow_match(pkt)
+ match.in_port = of_ports[3]
+ match.wildcards = wildcards
+ msg.match = match
+ msg.buffer_id = 0xffffffff
+ msg.actions.add(acts[toggle])
+ flows[toggle].append(msg)
+
+ # Show two sample flows
+ pa_logger.debug(flows[0][0].show())
+ pa_logger.debug(flows[1][0].show())
+
+ # Install the first set of flows
+ for f_idx in range(flow_count):
+ rv = self.controller.message_send(flows[0][f_idx])
+ self.assertTrue(rv != -1, "Error installing flow %d" % f_idx)
+ do_barrier(self.controller)
+
+ pa_logger.info("Installed %d flows" % flow_count)
+
+ # Repeatedly modify all the flows back and forth
+ updates = 0
+ # Report status about 5 times
+ mod_val = (iter_count / 4) + 1
+ start = time.time()
+ for iter_idx in range(iter_count):
+ if not iter_idx % mod_val:
+ pa_logger.info("Flow Toggle: iter %d of %d. " %
+ (iter_idx, iter_count) +
+ "%d updates in %d secs" %
+ (updates, time.time() - start))
+ for toggle in range(2):
+ t_idx = 1 - toggle
+ for f_idx in range(flow_count):
+ rv = self.controller.message_send(flows[t_idx][f_idx])
+ updates += 1
+ self.assertTrue(rv != -1, "Error modifying flow %d" %
+ f_idx)
+ do_barrier(self.controller)
+
+ end = time.time()
+ divisor = end - start or (end - start + 1)
+ pa_logger.info("Flow toggle: %d iterations" % iter_count)
+ pa_logger.info(" %d flow mods in %d secs, %d mods/sec" %
+ (updates, end - start, updates/divisor))
+
+
# You can pick and choose these by commenting tests in or out
iter_classes = [
basic.PacketIn,
basic.PacketOut,
DirectPacket,
+ FlowToggle,
DirectTwoPorts,
- DirectMC,
+ DirectMCNonIngress,
AllWildcardMatch,
AllWildcardMatchTagged,
SingleWildcardMatch,
@@ -1208,6 +1293,12 @@
]
class IterCases(BaseMatchCase):
+ """
+ Iterate over a bunch of test cases
+
+ The cases come from the list above
+ """
+
def runTest(self):
count = test_param_get(self.config, 'iter_count', default=10)
tests_done = 0
@@ -1221,11 +1312,13 @@
test.inheritSetup(self)
test.runTest()
tests_done += 1
+ # Report update about every minute, between tests
if time.time() - last > 60:
last = time.time()
- print("IterCases: Ran %d tests in %d " %
- (tests_done, last - start) +
- "seconds so far")
+ pa_logger.info(
+ "IterCases: Iter %d of %d; Ran %d tests in %d " %
+ (idx, count, tests_done, last - start) +
+ "seconds so far")
stats = all_stats_get(self)
last = time.time()
pa_logger.info("\nIterCases ran %d tests in %d seconds." %
diff --git a/tools/pylibopenflow/include/Put C header files here... b/tests/profiles/__init__.py
similarity index 100%
copy from tools/pylibopenflow/include/Put C header files here...
copy to tests/profiles/__init__.py
diff --git a/tests/profiles/example.py b/tests/profiles/example.py
new file mode 100644
index 0000000..938a6f2
--- /dev/null
+++ b/tests/profiles/example.py
@@ -0,0 +1,36 @@
+"""
+Sample profile
+
+A profile determines run specific behavior. It is meant to capture
+variations between different switch targets.
+
+A profile defines two target specific variables.
+
+1. A set of tests to skip
+
+TO BE IMPLEMENTED:
+
+2. A set of tests to run (overriding the default "skip" priority)
+optionally specifying a test parameters specific to the test run
+
+This file should be imported "as profile" so references to the
+module will properly map.
+
+@todo Allow a test to be run multiple times with different params
+"""
+
+#@var skip_test_list The list of tests to skip for this run
+skip_test_list = []
+
+# TO BE IMPLEMENTED
+# A list of test cases with parameters(?)
+# TBD
+#@var run_test_list List of tests to run which would normally be skipped
+run_test_list = dict(
+ # Example
+ # SomeTestCase = [dict(<params1>), dict(<params2>),...],
+)
+
+# for test_dict in profile.run_test_list:
+# for test_name, test_params in test_dict.items():
+# ...
diff --git a/tests/profiles/noing.py b/tests/profiles/noing.py
new file mode 100644
index 0000000..c9abc63
--- /dev/null
+++ b/tests/profiles/noing.py
@@ -0,0 +1,19 @@
+"""
+No-ingress action profile
+
+Profile for switch that does not support the IN_PORT port
+in the output action.
+
+We also don't run the port config modify test for this profile.
+"""
+
+#@var skip_test_list The list of tests to skip for this run
+skip_test_list = [
+ "PortConfigMod",
+ "FloodMinusPort",
+ "ModifyL2DstIngressMC",
+ "ModifyL2DstIngress",
+ "DirectMC",
+ "AllPlusIngress",
+ "FloodPlusIngress"
+]
diff --git a/tests/testutils.py b/tests/testutils.py
index 0a6c4bd..f1459bf 100644
--- a/tests/testutils.py
+++ b/tests/testutils.py
@@ -224,11 +224,11 @@
rv = controller.message_send(mod)
return rv
-def receive_pkt_check(dataplane, pkt, yes_ports, no_ports, assert_if, logger,
+def receive_pkt_check(dp, pkt, yes_ports, no_ports, assert_if, logger,
config):
"""
Check for proper receive packets across all ports
- @param dataplane The dataplane object
+ @param dp The dataplane object
@param pkt Expected packet; may be None if yes_ports is empty
@param yes_ports Set or list of ports that should recieve packet
@param no_ports Set or list of ports that should not receive packet
@@ -240,17 +240,20 @@
for ofport in yes_ports:
logger.debug("Checking for pkt on port " + str(ofport))
- (rcv_port, rcv_pkt, pkt_time) = dataplane.poll(
+ (rcv_port, rcv_pkt, pkt_time) = dp.poll(
port_number=ofport, timeout=1, exp_pkt=exp_pkt_arg)
assert_if.assertTrue(rcv_pkt is not None,
"Did not receive pkt on " + str(ofport))
- assert_if.assertEqual(str(pkt), str(rcv_pkt),
- "Response packet does not match send packet " +
- "on port " + str(ofport))
+ if not dataplane.match_exp_pkt(pkt, rcv_pkt):
+ logger.debug("Sent %s" % format_packet(pkt))
+ logger.debug("Resp %s" % format_packet(rcv_pkt))
+ assert_if.assertTrue(dataplane.match_exp_pkt(pkt, rcv_pkt),
+ "Response packet does not match send packet " +
+ "on port " + str(ofport))
for ofport in no_ports:
logger.debug("Negative check for pkt on port " + str(ofport))
- (rcv_port, rcv_pkt, pkt_time) = dataplane.poll(
+ (rcv_port, rcv_pkt, pkt_time) = dp.poll(
port_number=ofport, timeout=1, exp_pkt=exp_pkt_arg)
assert_if.assertTrue(rcv_pkt is None,
"Unexpected pkt on port " + str(ofport))
@@ -817,3 +820,26 @@
rv["matched"] += obj.matched_count
return rv
+
+FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.'
+ for x in range(256)])
+
+def hex_dump_buffer(src, length=16):
+ """
+ Convert src to a hex dump string and return the string
+ @param src The source buffer
+ @param length The number of bytes shown in each line
+ @returns A string showing the hex dump
+ """
+ result = []
+ for i in xrange(0, len(src), length):
+ chars = src[i:i+length]
+ hex = ' '.join(["%02x" % ord(x) for x in chars])
+ printable = ''.join(["%s" % ((ord(x) <= 127 and
+ FILTER[ord(x)]) or '.') for x in chars])
+ result.append("%04x %-*s %s\n" % (i, length*3, hex, printable))
+ return ''.join(result)
+
+def format_packet(pkt):
+ return "Packet length %d \n%s" % (len(str(pkt)),
+ hex_dump_buffer(str(pkt)))
diff --git a/tools/pylibopenflow/include/Put C header files here... b/tools/pylibopenflow/include/Put_C_header_files_here
similarity index 100%
rename from tools/pylibopenflow/include/Put C header files here...
rename to tools/pylibopenflow/include/Put_C_header_files_here