Merge pull request #13 from InCNTRE/master
Conformance Test-Suite 3
diff --git a/src/python/oftest/illegal_message.py b/src/python/oftest/illegal_message.py
new file mode 100644
index 0000000..0c6a878
--- /dev/null
+++ b/src/python/oftest/illegal_message.py
@@ -0,0 +1,108 @@
+"""
+Support an illegal message
+"""
+
+from cstruct import *
+
+ILLEGAL_MESSAGE_TYPE=217
+
+class illegal_message_type:
+ """
+ Wrapper class for illegal message
+
+ OpenFlow message header: length, version, xid, type
+ @arg length: The total length of the message
+ @arg version: The OpenFlow version (1)
+ @arg xid: The transaction ID
+ @arg type: The message type (OFPT_ECHO_REQUEST=2)
+
+ @arg data: Binary string following message members
+
+ The message type is set to "illegal" and the pack assert
+ check for the OF header is disabled
+ """
+
+ def __init__(self):
+ self.header = ofp_header()
+ self.header.type = ILLEGAL_MESSAGE_TYPE
+ self.data = ""
+
+ def pack(self):
+ """
+ Pack object into string
+
+ @return The packed string which can go on the wire
+
+ """
+ self.header.length = len(self)
+ packed = self.header.pack(assertstruct=False)
+
+ packed += self.data
+ return packed
+
+ def unpack(self, binary_string):
+ """
+ Unpack object from a binary string
+
+ @param binary_string The wire protocol byte string holding the object
+ represented as an array of bytes.
+ @return The remainder of binary_string that was not parsed.
+
+ """
+ binary_string = self.header.unpack(binary_string)
+
+ self.data = binary_string
+ binary_string = ''
+ return binary_string
+
+ def __len__(self):
+ """
+ Return the length of this object once packed into a string
+
+ @return An integer representing the number bytes in the packed
+ string.
+
+ """
+ length = OFP_HEADER_BYTES
+
+ length += len(self.data)
+ return length
+
+ def show(self, prefix=''):
+ """
+ Generate a string (with multiple lines) describing the contents
+ of the object in a readable manner
+
+ @param prefix Pre-pended at the beginning of each line.
+
+ """
+
+ outstr = prefix + 'illegal_message (' + \
+ str(ILLEGAL_MESSAGE_TYPE) + ')\n'
+ prefix += ' '
+ outstr += prefix + 'ofp header\n'
+ outstr += self.header.show(prefix + ' ')
+ outstr += prefix + 'data is of length ' + str(len(self.data)) + '\n'
+ return outstr
+
+ def __eq__(self, other):
+ """
+ Return True if self and other hold the same data
+
+ @param other Other object in comparison
+
+ """
+ if type(self) != type(other): return False
+ if not self.header.__eq__(other.header): return False
+
+ if self.data != other.data: return False
+ return True
+
+ def __ne__(self, other):
+ """
+ Return True if self and other do not hold the same data
+
+ @param other Other object in comparison
+
+ """
+ return not self.__eq__(other)
diff --git a/tests/basic.py b/tests/basic.py
index feda04f..0171766 100644
--- a/tests/basic.py
+++ b/tests/basic.py
@@ -29,6 +29,8 @@
import oftest.dataplane as dataplane
import oftest.action as action
+import oftest.illegal_message as illegal_message
+
from testutils import *
#@var basic_port_map Local copy of the configuration map from OF port
@@ -90,6 +92,10 @@
#@todo Add an option to wait for a pkt transaction to ensure version
# compatibilty?
self.controller.connect(timeout=20)
+
+ # By default, respond to echo requests
+ self.controller.keep_alive = True
+
if not self.controller.active:
raise Exception("Controller startup failed")
if self.controller.switch_addr is None:
@@ -97,6 +103,8 @@
basic_logger.info("Connected " + str(self.controller.switch_addr))
request = message.features_request()
reply, pkt = self.controller.transact(request, timeout=10)
+ self.assertTrue(reply is not None,
+ "Did not complete features_request for handshake")
self.supported_actions = reply.actions
basic_logger.info("Supported actions: " + hex(self.supported_actions))
@@ -211,6 +219,8 @@
# self.dataplane.show()
# Would like an assert that checks the data plane
+test_prio["DataPlaneOnly"] = -1
+
class Echo(SimpleProtocol):
"""
Test echo response with no data
@@ -218,6 +228,8 @@
def runTest(self):
request = message.echo_request()
response, pkt = self.controller.transact(request)
+ self.assertTrue(response is not None,
+ "Did not get echo reply")
self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
'response is not echo_reply')
self.assertEqual(request.header.xid, response.header.xid,
@@ -232,6 +244,8 @@
request = message.echo_request()
request.data = 'OpenFlow Will Rule The World'
response, pkt = self.controller.transact(request)
+ self.assertTrue(response is not None,
+ "Did not get echo reply (with data)")
self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
'response is not echo_reply')
self.assertEqual(request.header.xid, response.header.xid,
@@ -471,7 +485,8 @@
request.table_id = 0xff
request.match.wildcards = 0 # ofp.OFPFW_ALL
response, pkt = self.controller.transact(request)
- self.assertTrue(response is not None, "Did not get response")
+ self.assertTrue(response is not None,
+ "Did not get response for flow stats")
basic_logger.debug(response.show())
test_prio["FlowStatsGet"] = -1
@@ -492,7 +507,8 @@
basic_logger.info("Sending table stats request")
request = message.table_stats_request()
response, pkt = self.controller.transact(request)
- self.assertTrue(response is not None, "Did not get response")
+ self.assertTrue(response is not None,
+ "Did not get reply for table stats")
basic_logger.debug(response.show())
class DescStatsGet(SimpleProtocol):
@@ -507,7 +523,8 @@
basic_logger.info("Sending stats request")
request = message.desc_stats_request()
response, pkt = self.controller.transact(request)
- self.assertTrue(response is not None, "Did not get response")
+ self.assertTrue(response is not None,
+ "Did not get reply for desc stats")
basic_logger.debug(response.show())
class FlowMod(SimpleProtocol):
@@ -602,5 +619,23 @@
self.assertTrue(response is not None, 'Did not receive error message')
+class BadMessage(SimpleProtocol):
+ """
+ Send a message with a bad type and verify an error is returned
+ """
+
+ def runTest(self):
+ basic_logger.info("Running " + str(self))
+ request = illegal_message.illegal_message_type()
+
+ reply, pkt = self.controller.transact(request, timeout=10)
+ self.assertTrue(reply is not None, "Did not get response to bad req")
+ self.assertTrue(reply.header.type == ofp.OFPT_ERROR,
+ "reply not an error message")
+ self.assertTrue(reply.type == ofp.OFPET_BAD_REQUEST,
+ "reply error type is not bad request")
+ self.assertTrue(reply.code == ofp.OFPBRC_BAD_TYPE,
+ "reply error code is not bad type")
+
if __name__ == "__main__":
print "Please run through oft script: ./oft --test_spec=basic"
diff --git a/tests/pktact.py b/tests/pktact.py
index 14f9e8f..aca6766 100644
--- a/tests/pktact.py
+++ b/tests/pktact.py
@@ -1888,6 +1888,88 @@
"""
test_prio["MixedVLAN"] = -1
-
+
+class MatchEach(basic.SimpleDataPlane):
+ """
+ Check that each match field is actually matched on.
+ Installs two flows that differ in one field. The flow that should not
+ match has a higher priority, so if that field is ignored during matching
+ the packet will be sent out the wrong port.
+
+ TODO test UDP, ARP, ICMP, etc.
+ """
+ def runTest(self):
+ of_ports = pa_port_map.keys()
+ of_ports.sort()
+ self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
+
+ delete_all_flows(self.controller, pa_logger)
+
+ pkt = simple_tcp_packet()
+ ingress_port = of_ports[0]
+ egress_port = of_ports[1]
+
+ def testField(field, mask):
+ pa_logger.info("Testing field %s" % field)
+
+ def addFlow(matching, priority, output_port):
+ match = packet_to_flow_match(self, pkt)
+ self.assertTrue(match is not None, "Could not generate flow match from pkt")
+ match.wildcards &= ~ofp.OFPFW_IN_PORT
+ match.in_port = ingress_port
+ if not matching:
+ # Make sure flow doesn't match
+ orig = getattr(match, field)
+ if isinstance(orig, list):
+ new = map(lambda a: ~a[0] & a[1], zip(orig, mask))
+ else:
+ new = ~orig & mask
+ setattr(match, field, new)
+ request = message.flow_mod()
+ request.match = match
+ request.buffer_id = 0xffffffff
+ request.priority = priority
+ act = action.action_output()
+ act.port = output_port
+ self.assertTrue(request.actions.add(act), "Could not add action")
+ pa_logger.info("Inserting flow")
+ self.controller.message_send(request)
+
+ # This flow should match.
+ addFlow(matching=True, priority=0, output_port=egress_port)
+ # This flow should not match, but it has a higher priority.
+ addFlow(matching=False, priority=1, output_port=ofp.OFPP_IN_PORT)
+
+ self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
+
+ pa_logger.info("Sending packet to dp port " + str(ingress_port))
+ self.dataplane.send(ingress_port, str(pkt))
+
+ exp_pkt_arg = None
+ exp_port = None
+ if pa_config["relax"]:
+ exp_pkt_arg = pkt
+ exp_port = egress_port
+
+ (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(port_number=exp_port,
+ exp_pkt=exp_pkt_arg)
+ self.assertTrue(rcv_pkt is not None, "Did not receive packet")
+ pa_logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " + str(rcv_port))
+ self.assertEqual(rcv_port, egress_port, "Unexpected receive port")
+ self.assertEqual(str(pkt), str(rcv_pkt), 'Response packet does not match send packet')
+
+ # TODO in_port
+ testField("dl_src", [0xff]*6)
+ testField("dl_dst", [0xff]*6)
+ testField("dl_type", 0xffff)
+ testField("dl_vlan", 0xfff)
+ # TODO dl_vlan_pcp
+ testField("nw_src", 0xffffffff)
+ testField("nw_dst", 0xffffffff)
+ testField("nw_tos", 0x3f)
+ testField("nw_proto", 0xff)
+ testField("tp_src", 0xffff)
+ testField("tp_dst", 0xffff)
+
if __name__ == "__main__":
print "Please run through oft script: ./oft --test_spec=basic"
diff --git a/tests/serial_failover.py b/tests/serial_failover.py
index 90a4236..8ae5a46 100644
--- a/tests/serial_failover.py
+++ b/tests/serial_failover.py
@@ -80,11 +80,15 @@
self.controller.start()
#@todo Add an option to wait for a pkt transaction to ensure version
# compatibilty?
- self.controller.connect()
+ self.controller.connect(timeout=10)
self.assertTrue(self.controller.active,
"Controller startup failed, not active")
self.assertTrue(self.controller.switch_addr is not None,
"Controller startup failed, no switch addr")
+ request = message.features_request()
+ reply, pkt = self.controller.transact(request, timeout=10)
+ self.assertTrue(reply is not None,
+ "Did not complete features_request for handshake")
serial_failover_logger.info("Connected " +
str(self.controller.switch_addr))
@@ -117,7 +121,7 @@
# controller_list is list of ip/port tuples
partial_list = test_param_get(serial_failover_config,
'controller_list')
- serial_failover_logger.debug(str(partial_list))
+ serial_failover_logger.debug("ctrl list: " + str(partial_list))
self.controller_list = [(serial_failover_config["controller_host"],
serial_failover_config["controller_port"])]
if partial_list is not None: