blob: db05320fa49d61f584190bcbcb385c6083e75b6c [file] [log] [blame]
Dan Talaycodba244e2010-02-15 14:08:53 -08001"""
Dan Talayco79f36082010-03-11 16:53:53 -08002Basic protocol and dataplane test cases
Dan Talaycodba244e2010-02-15 14:08:53 -08003
Dan Talayco48370102010-03-03 15:17:33 -08004It is recommended that these definitions be kept in their own
5namespace as different groups of tests will likely define
6similar identifiers.
7
Dan Talaycodba244e2010-02-15 14:08:53 -08008Current Assumptions:
9
Dan Talaycodba244e2010-02-15 14:08:53 -080010 The switch is actively attempting to contact the controller at the address
Rich Lane477f4812012-10-04 22:49:00 -070011indicated in oftest.config.
Dan Talaycodba244e2010-02-15 14:08:53 -080012
13"""
14
Dan Talaycodba244e2010-02-15 14:08:53 -080015import time
16import sys
Dan Talayco48370102010-03-03 15:17:33 -080017import logging
Dan Talaycodba244e2010-02-15 14:08:53 -080018
Dan Talayco2c0dba32010-03-06 22:47:06 -080019import unittest
Ken Chiang1bf01602012-04-04 10:48:23 -070020import random
Dan Talayco2c0dba32010-03-06 22:47:06 -080021
Rich Lane477f4812012-10-04 22:49:00 -070022from oftest import config
Dan Talayco2c0dba32010-03-06 22:47:06 -080023import oftest.controller as controller
24import oftest.cstruct as ofp
25import oftest.message as message
26import oftest.dataplane as dataplane
27import oftest.action as action
28
Dan Talaycoe605b1b2012-09-18 06:56:20 -070029import oftest.illegal_message as illegal_message
30
Rich Laneda3b5ad2012-10-03 09:05:32 -070031from oftest.testutils import *
Dan Talayco6ce963a2010-03-07 21:58:13 -080032
Ken Chiangaeb23d62012-08-23 21:20:07 -070033TEST_VID_DEFAULT = 2
34
Dan Talayco6ce963a2010-03-07 21:58:13 -080035class SimpleProtocol(unittest.TestCase):
Dan Talaycodba244e2010-02-15 14:08:53 -080036 """
37 Root class for setting up the controller
38 """
39
Rich Laned1d9c282012-10-04 22:07:10 -070040 priority = 1
41
Dan Talaycodba244e2010-02-15 14:08:53 -080042 def setUp(self):
Rich Lane9a003812012-10-04 17:17:59 -070043 logging.info("** START TEST CASE " + str(self))
Dan Talayco2c0dba32010-03-06 22:47:06 -080044 self.controller = controller.Controller(
Rich Lane477f4812012-10-04 22:49:00 -070045 host=config["controller_host"],
46 port=config["controller_port"])
Dan Talaycof8f41402010-03-12 22:17:39 -080047 # clean_shutdown should be set to False to force quit app
Dan Talayco2c0dba32010-03-06 22:47:06 -080048 self.clean_shutdown = True
Dan Talayco710438c2010-02-18 15:16:07 -080049 self.controller.start()
Dan Talaycoef701f42010-05-07 09:22:35 -070050 #@todo Add an option to wait for a pkt transaction to ensure version
51 # compatibilty?
Dan Talayco710438c2010-02-18 15:16:07 -080052 self.controller.connect(timeout=20)
Dan Talayco5d0f7cc2012-09-19 11:28:58 -070053
54 # By default, respond to echo requests
55 self.controller.keep_alive = True
56
Dan Talaycoef701f42010-05-07 09:22:35 -070057 if not self.controller.active:
Rich Lane58cf05f2012-07-11 16:41:47 -070058 raise Exception("Controller startup failed")
Dan Talayco677c0b72011-08-23 22:53:38 -070059 if self.controller.switch_addr is None:
Rich Lane58cf05f2012-07-11 16:41:47 -070060 raise Exception("Controller startup failed (no switch addr)")
Rich Lane9a003812012-10-04 17:17:59 -070061 logging.info("Connected " + str(self.controller.switch_addr))
Ed Swierkc7193a22012-08-22 06:51:02 -070062 request = message.features_request()
Dan Talaycoc689a792012-09-28 14:22:53 -070063 reply, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -070064 self.assertTrue(reply is not None,
65 "Did not complete features_request for handshake")
Ed Swierkc7193a22012-08-22 06:51:02 -070066 self.supported_actions = reply.actions
Rich Lane9a003812012-10-04 17:17:59 -070067 logging.info("Supported actions: " + hex(self.supported_actions))
Dan Talaycodba244e2010-02-15 14:08:53 -080068
Dan Talayco677cc112012-03-27 10:28:58 -070069 def inheritSetup(self, parent):
70 """
71 Inherit the setup of a parent
72
73 This allows running at test from within another test. Do the
74 following:
75
76 sub_test = SomeTestClass() # Create an instance of the test class
77 sub_test.inheritSetup(self) # Inherit setup of parent
78 sub_test.runTest() # Run the test
79
80 Normally, only the parent's setUp and tearDown are called and
81 the state after the sub_test is run must be taken into account
82 by subsequent operations.
83 """
Rich Lane9a003812012-10-04 17:17:59 -070084 logging.info("** Setup " + str(self) + " inheriting from "
Dan Talayco677cc112012-03-27 10:28:58 -070085 + str(parent))
86 self.controller = parent.controller
Ed Swierk6192e512012-08-22 11:41:40 -070087 self.supported_actions = parent.supported_actions
Dan Talayco677cc112012-03-27 10:28:58 -070088
Dan Talaycodba244e2010-02-15 14:08:53 -080089 def tearDown(self):
Rich Lane9a003812012-10-04 17:17:59 -070090 logging.info("** END TEST CASE " + str(self))
Dan Talaycodba244e2010-02-15 14:08:53 -080091 self.controller.shutdown()
Dan Talayco2c0dba32010-03-06 22:47:06 -080092 #@todo Review if join should be done on clean_shutdown
Dan Talaycof8f41402010-03-12 22:17:39 -080093 if self.clean_shutdown:
94 self.controller.join()
Dan Talaycodba244e2010-02-15 14:08:53 -080095
96 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -080097 # Just a simple sanity check as illustration
Rich Lane9a003812012-10-04 17:17:59 -070098 logging.info("Running simple proto test")
Dan Talayco710438c2010-02-18 15:16:07 -080099 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800100 str(self) + 'No connection to switch')
101
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700102 def assertTrue(self, cond, msg):
103 if not cond:
Rich Lane9a003812012-10-04 17:17:59 -0700104 logging.error("** FAILED ASSERTION: " + msg)
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700105 unittest.TestCase.assertTrue(self, cond, msg)
106
Dan Talayco6ce963a2010-03-07 21:58:13 -0800107class SimpleDataPlane(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800108 """
109 Root class that sets up the controller and dataplane
110 """
111 def setUp(self):
Dan Talayco6ce963a2010-03-07 21:58:13 -0800112 SimpleProtocol.setUp(self)
Rich Lane477f4812012-10-04 22:49:00 -0700113 self.dataplane = dataplane.DataPlane(config)
114 for of_port, ifname in config["port_map"].items():
Dan Talaycodba244e2010-02-15 14:08:53 -0800115 self.dataplane.port_add(ifname, of_port)
116
Dan Talayco677cc112012-03-27 10:28:58 -0700117 def inheritSetup(self, parent):
118 """
119 Inherit the setup of a parent
120
121 See SimpleProtocol.inheritSetup
122 """
123 SimpleProtocol.inheritSetup(self, parent)
124 self.dataplane = parent.dataplane
125
Dan Talaycodba244e2010-02-15 14:08:53 -0800126 def tearDown(self):
Rich Lane9a003812012-10-04 17:17:59 -0700127 logging.info("Teardown for simple dataplane test")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800128 SimpleProtocol.tearDown(self)
Rich Lane58cf05f2012-07-11 16:41:47 -0700129 if hasattr(self, 'dataplane'):
130 self.dataplane.kill(join_threads=self.clean_shutdown)
Rich Lane9a003812012-10-04 17:17:59 -0700131 logging.info("Teardown done")
Dan Talaycodba244e2010-02-15 14:08:53 -0800132
133 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -0800134 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800135 str(self) + 'No connection to switch')
136 # self.dataplane.show()
137 # Would like an assert that checks the data plane
138
Dan Talayco551befa2010-07-15 17:05:32 -0700139class DataPlaneOnly(unittest.TestCase):
140 """
141 Root class that sets up only the dataplane
142 """
143
Rich Laned1d9c282012-10-04 22:07:10 -0700144 priority = -1
145
Dan Talayco551befa2010-07-15 17:05:32 -0700146 def setUp(self):
Shudong Zhoue3582a52012-08-03 20:46:50 -0700147 self.clean_shutdown = True
Rich Lane9a003812012-10-04 17:17:59 -0700148 logging.info("** START DataPlaneOnly CASE " + str(self))
Rich Lane477f4812012-10-04 22:49:00 -0700149 self.dataplane = dataplane.DataPlane(config)
150 for of_port, ifname in config["port_map"].items():
Dan Talayco551befa2010-07-15 17:05:32 -0700151 self.dataplane.port_add(ifname, of_port)
152
153 def tearDown(self):
Rich Lane9a003812012-10-04 17:17:59 -0700154 logging.info("Teardown for simple dataplane test")
Dan Talayco551befa2010-07-15 17:05:32 -0700155 self.dataplane.kill(join_threads=self.clean_shutdown)
Rich Lane9a003812012-10-04 17:17:59 -0700156 logging.info("Teardown done")
Dan Talayco551befa2010-07-15 17:05:32 -0700157
158 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700159 logging.info("DataPlaneOnly")
Dan Talayco285a8382010-07-20 14:06:55 -0700160 # self.dataplane.show()
Dan Talayco551befa2010-07-15 17:05:32 -0700161 # Would like an assert that checks the data plane
162
Dan Talayco6ce963a2010-03-07 21:58:13 -0800163class Echo(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800164 """
165 Test echo response with no data
166 """
167 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800168 request = message.echo_request()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800169 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700170 self.assertTrue(response is not None,
171 "Did not get echo reply")
Dan Talayco2c0dba32010-03-06 22:47:06 -0800172 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800173 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800174 self.assertEqual(request.header.xid, response.header.xid,
175 'response xid != request xid')
176 self.assertEqual(len(response.data), 0, 'response data non-empty')
177
Dan Talayco6ce963a2010-03-07 21:58:13 -0800178class EchoWithData(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800179 """
180 Test echo response with short string data
181 """
182 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800183 request = message.echo_request()
Dan Talaycodba244e2010-02-15 14:08:53 -0800184 request.data = 'OpenFlow Will Rule The World'
Dan Talaycoe226eb12010-02-18 23:06:30 -0800185 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700186 self.assertTrue(response is not None,
187 "Did not get echo reply (with data)")
Dan Talayco2c0dba32010-03-06 22:47:06 -0800188 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800189 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800190 self.assertEqual(request.header.xid, response.header.xid,
191 'response xid != request xid')
192 self.assertEqual(request.data, response.data,
193 'response data does not match request')
194
Dan Talayco6ce963a2010-03-07 21:58:13 -0800195class PacketIn(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800196 """
197 Test packet in function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800198
199 Send a packet to each dataplane port and verify that a packet
200 in message is received from the controller for each
Dan Talaycodba244e2010-02-15 14:08:53 -0800201 """
202 def runTest(self):
203 # Construct packet to send to dataplane
Dan Talaycoe226eb12010-02-18 23:06:30 -0800204 # Send packet to dataplane, once to each port
Dan Talaycodba244e2010-02-15 14:08:53 -0800205 # Poll controller with expect message type packet in
Dan Talaycoe226eb12010-02-18 23:06:30 -0800206
Rich Lane9a003812012-10-04 17:17:59 -0700207 rc = delete_all_flows(self.controller)
Dan Talayco6ce963a2010-03-07 21:58:13 -0800208 self.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700209 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800210
Rich Lane477f4812012-10-04 22:49:00 -0700211 vid = test_param_get(config, 'vid', default=TEST_VID_DEFAULT)
Ken Chiangaeb23d62012-08-23 21:20:07 -0700212
Rich Lane477f4812012-10-04 22:49:00 -0700213 for of_port in config["port_map"].keys():
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700214 for pkt, pt in [
215 (simple_tcp_packet(), "simple TCP packet"),
Ken Chiangaeb23d62012-08-23 21:20:07 -0700216 (simple_tcp_packet(dl_vlan_enable=True,dl_vlan=vid,pktlen=108),
217 "simple tagged TCP packet"),
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700218 (simple_eth_packet(), "simple Ethernet packet"),
219 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
Dan Talaycodba244e2010-02-15 14:08:53 -0800220
Rich Lane9a003812012-10-04 17:17:59 -0700221 logging.info("PKT IN test with %s, port %s" % (pt, of_port))
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700222 self.dataplane.send(of_port, str(pkt))
223 #@todo Check for unexpected messages?
224 count = 0
225 while True:
Dan Talaycoc689a792012-09-28 14:22:53 -0700226 (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN)
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700227 if not response: # Timeout
228 break
Ed Swierk506614a2012-03-29 08:16:59 -0700229 if dataplane.match_exp_pkt(pkt, response.data): # Got match
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700230 break
Rich Lane477f4812012-10-04 22:49:00 -0700231 if not config["relax"]: # Only one attempt to match
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700232 break
233 count += 1
234 if count > 10: # Too many tries
235 break
Dan Talayco48370102010-03-03 15:17:33 -0800236
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700237 self.assertTrue(response is not None,
238 'Packet in message not received on port ' +
239 str(of_port))
Ed Swierk506614a2012-03-29 08:16:59 -0700240 if not dataplane.match_exp_pkt(pkt, response.data):
Rich Lane9a003812012-10-04 17:17:59 -0700241 logging.debug("Sent %s" % format_packet(pkt))
242 logging.debug("Resp %s" % format_packet(response.data))
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700243 self.assertTrue(False,
244 'Response packet does not match send packet' +
245 ' for port ' + str(of_port))
Dan Talaycodba244e2010-02-15 14:08:53 -0800246
Ed Swierk3ae7f712012-08-22 06:45:25 -0700247class PacketInDefaultDrop(SimpleDataPlane):
248 """
249 Test packet in function
250
251 Send a packet to each dataplane port and verify that a packet
252 in message is received from the controller for each
253 """
Rich Laned1d9c282012-10-04 22:07:10 -0700254
255 priority = -1
256
Ed Swierk3ae7f712012-08-22 06:45:25 -0700257 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700258 rc = delete_all_flows(self.controller)
Ed Swierk3ae7f712012-08-22 06:45:25 -0700259 self.assertEqual(rc, 0, "Failed to delete all flows")
260 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
261
Rich Lane477f4812012-10-04 22:49:00 -0700262 for of_port in config["port_map"].keys():
Ed Swierk3ae7f712012-08-22 06:45:25 -0700263 pkt = simple_tcp_packet()
264 self.dataplane.send(of_port, str(pkt))
265 count = 0
266 while True:
Dan Talaycoc689a792012-09-28 14:22:53 -0700267 (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN)
Ed Swierk3ae7f712012-08-22 06:45:25 -0700268 if not response: # Timeout
269 break
270 if dataplane.match_exp_pkt(pkt, response.data): # Got match
271 break
Rich Lane477f4812012-10-04 22:49:00 -0700272 if not config["relax"]: # Only one attempt to match
Ed Swierk3ae7f712012-08-22 06:45:25 -0700273 break
274 count += 1
275 if count > 10: # Too many tries
276 break
277
278 self.assertTrue(response is None,
279 'Packet in message received on port ' +
280 str(of_port))
281
Dan Talayco1f648cb2012-05-03 09:37:56 -0700282class PacketInBroadcastCheck(SimpleDataPlane):
283 """
284 Check if bcast pkts leak when no flows are present
285
286 Clear the flow table
287 Send in a broadcast pkt
288 Look for the packet on other dataplane ports.
289 """
Rich Laned1d9c282012-10-04 22:07:10 -0700290
291 priority = -1
292
Dan Talayco1f648cb2012-05-03 09:37:56 -0700293 def runTest(self):
294 # Need at least two ports
Rich Lane477f4812012-10-04 22:49:00 -0700295 self.assertTrue(len(config["port_map"]) > 1, "Too few ports for test")
Dan Talayco1f648cb2012-05-03 09:37:56 -0700296
Rich Lane9a003812012-10-04 17:17:59 -0700297 rc = delete_all_flows(self.controller)
Dan Talayco1f648cb2012-05-03 09:37:56 -0700298 self.assertEqual(rc, 0, "Failed to delete all flows")
299 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
300
Rich Lane477f4812012-10-04 22:49:00 -0700301 of_ports = config["port_map"].keys()
Dan Talayco1f648cb2012-05-03 09:37:56 -0700302 d_port = of_ports[0]
303 pkt = simple_eth_packet(dl_dst='ff:ff:ff:ff:ff:ff')
304
Rich Lane9a003812012-10-04 17:17:59 -0700305 logging.info("BCast Leak Test, send to port %s" % d_port)
Dan Talayco1f648cb2012-05-03 09:37:56 -0700306 self.dataplane.send(d_port, str(pkt))
307
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700308 (of_port, pkt_in, pkt_time) = self.dataplane.poll(exp_pkt=pkt)
Dan Talayco1f648cb2012-05-03 09:37:56 -0700309 self.assertTrue(pkt_in is None,
310 'BCast packet received on port ' + str(of_port))
311
Dan Talayco6ce963a2010-03-07 21:58:13 -0800312class PacketOut(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800313 """
314 Test packet out function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800315
316 Send packet out message to controller for each dataplane port and
317 verify the packet appears on the appropriate dataplane port
Dan Talaycodba244e2010-02-15 14:08:53 -0800318 """
319 def runTest(self):
320 # Construct packet to send to dataplane
321 # Send packet to dataplane
322 # Poll controller with expect message type packet in
Dan Talayco41eae8b2010-03-10 13:57:06 -0800323
Rich Lane9a003812012-10-04 17:17:59 -0700324 rc = delete_all_flows(self.controller)
Dan Talayco41eae8b2010-03-10 13:57:06 -0800325 self.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talaycodba244e2010-02-15 14:08:53 -0800326
327 # These will get put into function
Rich Lane477f4812012-10-04 22:49:00 -0700328 of_ports = config["port_map"].keys()
Dan Talayco48370102010-03-03 15:17:33 -0800329 of_ports.sort()
330 for dp_port in of_ports:
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700331 for outpkt, opt in [
332 (simple_tcp_packet(), "simple TCP packet"),
333 (simple_eth_packet(), "simple Ethernet packet"),
334 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
Dan Talaycodba244e2010-02-15 14:08:53 -0800335
Rich Lane9a003812012-10-04 17:17:59 -0700336 logging.info("PKT OUT test with %s, port %s" % (opt, dp_port))
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700337 msg = message.packet_out()
338 msg.data = str(outpkt)
339 act = action.action_output()
340 act.port = dp_port
341 self.assertTrue(msg.actions.add(act), 'Could not add action to msg')
Dan Talaycodba244e2010-02-15 14:08:53 -0800342
Rich Lane9a003812012-10-04 17:17:59 -0700343 logging.info("PacketOut to: " + str(dp_port))
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700344 rv = self.controller.message_send(msg)
345 self.assertTrue(rv == 0, "Error sending out message")
Dan Talaycodba244e2010-02-15 14:08:53 -0800346
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700347 exp_pkt_arg = None
348 exp_port = None
Rich Lane477f4812012-10-04 22:49:00 -0700349 if config["relax"]:
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700350 exp_pkt_arg = outpkt
351 exp_port = dp_port
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700352 (of_port, pkt, pkt_time) = self.dataplane.poll(port_number=exp_port,
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700353 exp_pkt=exp_pkt_arg)
354
355 self.assertTrue(pkt is not None, 'Packet not received')
Rich Lane9a003812012-10-04 17:17:59 -0700356 logging.info("PacketOut: got pkt from " + str(of_port))
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700357 if of_port is not None:
358 self.assertEqual(of_port, dp_port, "Unexpected receive port")
Ed Swierk506614a2012-03-29 08:16:59 -0700359 if not dataplane.match_exp_pkt(outpkt, pkt):
Rich Lane9a003812012-10-04 17:17:59 -0700360 logging.debug("Sent %s" % format_packet(outpkt))
361 logging.debug("Resp %s" % format_packet(
Dan Talayco2baf8b52012-03-30 09:55:42 -0700362 str(pkt)[:len(str(outpkt))]))
Dan Talaycodc6fca32012-03-30 10:05:49 -0700363 self.assertEqual(str(outpkt), str(pkt)[:len(str(outpkt))],
364 'Response packet does not match send packet')
Dan Talaycodba244e2010-02-15 14:08:53 -0800365
Ken Chiang1bf01602012-04-04 10:48:23 -0700366class PacketOutMC(SimpleDataPlane):
367 """
368 Test packet out to multiple output ports
369
370 Send packet out message to controller for 1 to N dataplane ports and
371 verify the packet appears on the appropriate ports
372 """
373 def runTest(self):
374 # Construct packet to send to dataplane
375 # Send packet to dataplane
376 # Poll controller with expect message type packet in
377
Rich Lane9a003812012-10-04 17:17:59 -0700378 rc = delete_all_flows(self.controller)
Ken Chiang1bf01602012-04-04 10:48:23 -0700379 self.assertEqual(rc, 0, "Failed to delete all flows")
380
381 # These will get put into function
Rich Lane477f4812012-10-04 22:49:00 -0700382 of_ports = config["port_map"].keys()
Ken Chiang1bf01602012-04-04 10:48:23 -0700383 random.shuffle(of_ports)
384 for num_ports in range(1,len(of_ports)+1):
385 for outpkt, opt in [
386 (simple_tcp_packet(), "simple TCP packet"),
387 (simple_eth_packet(), "simple Ethernet packet"),
388 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
389
390 dp_ports = of_ports[0:num_ports]
Rich Lane9a003812012-10-04 17:17:59 -0700391 logging.info("PKT OUT test with " + opt +
Ken Chiang1bf01602012-04-04 10:48:23 -0700392 ", ports " + str(dp_ports))
393 msg = message.packet_out()
394 msg.data = str(outpkt)
395 act = action.action_output()
396 for i in range(0,num_ports):
397 act.port = dp_ports[i]
398 self.assertTrue(msg.actions.add(act),
399 'Could not add action to msg')
400
Rich Lane9a003812012-10-04 17:17:59 -0700401 logging.info("PacketOut to: " + str(dp_ports))
Ken Chiang1bf01602012-04-04 10:48:23 -0700402 rv = self.controller.message_send(msg)
403 self.assertTrue(rv == 0, "Error sending out message")
404
405 receive_pkt_check(self.dataplane, outpkt, dp_ports,
406 set(of_ports).difference(dp_ports),
Rich Lane477f4812012-10-04 22:49:00 -0700407 self, config)
Ken Chiang1bf01602012-04-04 10:48:23 -0700408
Dan Talayco6ce963a2010-03-07 21:58:13 -0800409class FlowStatsGet(SimpleProtocol):
410 """
411 Get stats
Dan Talayco2c0dba32010-03-06 22:47:06 -0800412
Dan Talayco6ce963a2010-03-07 21:58:13 -0800413 Simply verify stats get transaction
414 """
Rich Laned1d9c282012-10-04 22:07:10 -0700415
416 priority = -1
417
Dan Talayco6ce963a2010-03-07 21:58:13 -0800418 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700419 logging.info("Running StatsGet")
420 logging.info("Inserting trial flow")
Rich Lane477f4812012-10-04 22:49:00 -0700421 request = flow_mod_gen(config["port_map"], True)
Dan Talayco41eae8b2010-03-10 13:57:06 -0800422 rv = self.controller.message_send(request)
423 self.assertTrue(rv != -1, "Failed to insert test flow")
424
Rich Lane9a003812012-10-04 17:17:59 -0700425 logging.info("Sending flow request")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800426 request = message.flow_stats_request()
427 request.out_port = ofp.OFPP_NONE
Dan Talayco41eae8b2010-03-10 13:57:06 -0800428 request.table_id = 0xff
429 request.match.wildcards = 0 # ofp.OFPFW_ALL
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700430 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700431 self.assertTrue(response is not None,
432 "Did not get response for flow stats")
Rich Lane9a003812012-10-04 17:17:59 -0700433 logging.debug(response.show())
Dan Talayco6ce963a2010-03-07 21:58:13 -0800434
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700435class TableStatsGet(SimpleProtocol):
436 """
437 Get table stats
438
439 Simply verify table stats get transaction
440 """
441 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700442 logging.info("Running TableStatsGet")
443 logging.info("Inserting trial flow")
Rich Lane477f4812012-10-04 22:49:00 -0700444 request = flow_mod_gen(config["port_map"], True)
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700445 rv = self.controller.message_send(request)
446 self.assertTrue(rv != -1, "Failed to insert test flow")
447
Rich Lane9a003812012-10-04 17:17:59 -0700448 logging.info("Sending table stats request")
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700449 request = message.table_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700450 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700451 self.assertTrue(response is not None,
452 "Did not get reply for table stats")
Rich Lane9a003812012-10-04 17:17:59 -0700453 logging.debug(response.show())
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700454
Ed Swierkae74c362012-04-02 08:21:41 -0700455class DescStatsGet(SimpleProtocol):
456 """
457 Get stats
458
459 Simply verify stats get transaction
460 """
461 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700462 logging.info("Running DescStatsGet")
Ed Swierkae74c362012-04-02 08:21:41 -0700463
Rich Lane9a003812012-10-04 17:17:59 -0700464 logging.info("Sending stats request")
Ed Swierkae74c362012-04-02 08:21:41 -0700465 request = message.desc_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700466 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700467 self.assertTrue(response is not None,
468 "Did not get reply for desc stats")
Rich Lane9a003812012-10-04 17:17:59 -0700469 logging.debug(response.show())
Ed Swierkae74c362012-04-02 08:21:41 -0700470
Dan Talayco6ce963a2010-03-07 21:58:13 -0800471class FlowMod(SimpleProtocol):
472 """
473 Insert a flow
474
475 Simple verification of a flow mod transaction
476 """
477
478 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700479 logging.info("Running " + str(self))
Rich Lane477f4812012-10-04 22:49:00 -0700480 request = flow_mod_gen(config["port_map"], True)
Dan Talayco6ce963a2010-03-07 21:58:13 -0800481 rv = self.controller.message_send(request)
Dan Talayco41eae8b2010-03-10 13:57:06 -0800482 self.assertTrue(rv != -1, "Error installing flow mod")
483
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700484class PortConfigMod(SimpleProtocol):
485 """
486 Modify a bit in port config and verify changed
487
488 Get the switch configuration, modify the port configuration
489 and write it back; get the config again and verify changed.
490 Then set it back to the way it was.
491 """
492
493 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700494 logging.info("Running " + str(self))
Rich Lane477f4812012-10-04 22:49:00 -0700495 for of_port, ifname in config["port_map"].items(): # Grab first port
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700496 break
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700497
Rich Lane477f4812012-10-04 22:49:00 -0700498 (hw_addr, port_config, advert) = \
Rich Lane9a003812012-10-04 17:17:59 -0700499 port_config_get(self.controller, of_port)
Rich Lane477f4812012-10-04 22:49:00 -0700500 self.assertTrue(port_config is not None, "Did not get port config")
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700501
Rich Lane9a003812012-10-04 17:17:59 -0700502 logging.debug("No flood bit port " + str(of_port) + " is now " +
Rich Lane477f4812012-10-04 22:49:00 -0700503 str(port_config & ofp.OFPPC_NO_FLOOD))
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700504
505 rv = port_config_set(self.controller, of_port,
Rich Lane477f4812012-10-04 22:49:00 -0700506 port_config ^ ofp.OFPPC_NO_FLOOD, ofp.OFPPC_NO_FLOOD)
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700507 self.assertTrue(rv != -1, "Error sending port mod")
508
509 # Verify change took place with same feature request
Rich Lane477f4812012-10-04 22:49:00 -0700510 (hw_addr, port_config2, advert) = \
Rich Lane9a003812012-10-04 17:17:59 -0700511 port_config_get(self.controller, of_port)
512 logging.debug("No flood bit port " + str(of_port) + " is now " +
Rich Lane477f4812012-10-04 22:49:00 -0700513 str(port_config2 & ofp.OFPPC_NO_FLOOD))
514 self.assertTrue(port_config2 is not None, "Did not get port config2")
515 self.assertTrue(port_config2 & ofp.OFPPC_NO_FLOOD !=
516 port_config & ofp.OFPPC_NO_FLOOD,
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700517 "Bit change did not take")
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700518 # Set it back
Rich Lane477f4812012-10-04 22:49:00 -0700519 rv = port_config_set(self.controller, of_port, port_config,
Rich Lane9a003812012-10-04 17:17:59 -0700520 ofp.OFPPC_NO_FLOOD)
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700521 self.assertTrue(rv != -1, "Error sending port mod")
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700522
Ken Chiangaeb23d62012-08-23 21:20:07 -0700523class PortConfigModErr(SimpleProtocol):
524 """
525 Modify a bit in port config on an invalid port and verify
526 error message is received.
527 """
528
529 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700530 logging.info("Running " + str(self))
Ken Chiangaeb23d62012-08-23 21:20:07 -0700531
532 # pick a random bad port number
533 bad_port = random.randint(1, ofp.OFPP_MAX)
534 count = 0
Rich Lane477f4812012-10-04 22:49:00 -0700535 while (count < 50) and (bad_port in config["port_map"].keys()):
Ken Chiangaeb23d62012-08-23 21:20:07 -0700536 bad_port = random.randint(1, ofp.OFPP_MAX)
537 count = count + 1
538 self.assertTrue(count < 50, "Error selecting bad port")
Rich Lane9a003812012-10-04 17:17:59 -0700539 logging.info("Select " + str(bad_port) + " as invalid port")
Ken Chiangaeb23d62012-08-23 21:20:07 -0700540
541 rv = port_config_set(self.controller, bad_port,
Rich Lane9a003812012-10-04 17:17:59 -0700542 ofp.OFPPC_NO_FLOOD, ofp.OFPPC_NO_FLOOD)
Ken Chiangaeb23d62012-08-23 21:20:07 -0700543 self.assertTrue(rv != -1, "Error sending port mod")
544
545 # poll for error message
546 while True:
Dan Talaycoc689a792012-09-28 14:22:53 -0700547 (response, raw) = self.controller.poll(ofp.OFPT_ERROR)
Ken Chiangaeb23d62012-08-23 21:20:07 -0700548 if not response: # Timeout
549 break
550 if response.code == ofp.OFPPMFC_BAD_PORT:
Rich Lane9a003812012-10-04 17:17:59 -0700551 logging.info("Received error message with OFPPMFC_BAD_PORT code")
Ken Chiangaeb23d62012-08-23 21:20:07 -0700552 break
Rich Lane477f4812012-10-04 22:49:00 -0700553 if not config["relax"]: # Only one attempt to match
Ken Chiangaeb23d62012-08-23 21:20:07 -0700554 break
555 count += 1
556 if count > 10: # Too many tries
557 break
558
559 self.assertTrue(response is not None, 'Did not receive error message')
560
Dan Talaycoe605b1b2012-09-18 06:56:20 -0700561class BadMessage(SimpleProtocol):
562 """
563 Send a message with a bad type and verify an error is returned
564 """
565
566 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700567 logging.info("Running " + str(self))
Dan Talaycoe605b1b2012-09-18 06:56:20 -0700568 request = illegal_message.illegal_message_type()
569
Dan Talaycoc689a792012-09-28 14:22:53 -0700570 reply, pkt = self.controller.transact(request)
Dan Talaycoe605b1b2012-09-18 06:56:20 -0700571 self.assertTrue(reply is not None, "Did not get response to bad req")
572 self.assertTrue(reply.header.type == ofp.OFPT_ERROR,
573 "reply not an error message")
574 self.assertTrue(reply.type == ofp.OFPET_BAD_REQUEST,
575 "reply error type is not bad request")
576 self.assertTrue(reply.code == ofp.OFPBRC_BAD_TYPE,
577 "reply error code is not bad type")
578
Dan Talaycodba244e2010-02-15 14:08:53 -0800579if __name__ == "__main__":
Dan Talayco2c0dba32010-03-06 22:47:06 -0800580 print "Please run through oft script: ./oft --test_spec=basic"