blob: 01717666fd8c1bb9fe741f2664d9692b35e35a18 [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 Talayco41eae8b2010-03-10 13:57:06 -080010 The function test_set_init is called with a complete configuration
11dictionary prior to the invocation of any tests from this file.
12
Dan Talaycodba244e2010-02-15 14:08:53 -080013 The switch is actively attempting to contact the controller at the address
14indicated oin oft_config
15
16"""
17
Dan Talaycodba244e2010-02-15 14:08:53 -080018import time
Dan Talayco710438c2010-02-18 15:16:07 -080019import signal
Dan Talaycodba244e2010-02-15 14:08:53 -080020import sys
Dan Talayco48370102010-03-03 15:17:33 -080021import logging
Dan Talaycodba244e2010-02-15 14:08:53 -080022
Dan Talayco2c0dba32010-03-06 22:47:06 -080023import unittest
Ken Chiang1bf01602012-04-04 10:48:23 -070024import random
Dan Talayco2c0dba32010-03-06 22:47:06 -080025
26import oftest.controller as controller
27import oftest.cstruct as ofp
28import oftest.message as message
29import oftest.dataplane as dataplane
30import oftest.action as action
31
Dan Talaycoe605b1b2012-09-18 06:56:20 -070032import oftest.illegal_message as illegal_message
33
Dan Talayco6ce963a2010-03-07 21:58:13 -080034from testutils import *
35
36#@var basic_port_map Local copy of the configuration map from OF port
37# numbers to OS interfaces
Dan Talayco48370102010-03-03 15:17:33 -080038basic_port_map = None
Dan Talayco6ce963a2010-03-07 21:58:13 -080039#@var basic_logger Local logger object
Dan Talayco48370102010-03-03 15:17:33 -080040basic_logger = None
Dan Talayco6ce963a2010-03-07 21:58:13 -080041#@var basic_config Local copy of global configuration data
Dan Talayco48370102010-03-03 15:17:33 -080042basic_config = None
43
Dan Talaycoc24aaae2010-07-08 14:05:24 -070044test_prio = {}
45
Ken Chiangaeb23d62012-08-23 21:20:07 -070046TEST_VID_DEFAULT = 2
47
Dan Talayco48370102010-03-03 15:17:33 -080048def test_set_init(config):
49 """
50 Set up function for basic test classes
51
52 @param config The configuration dictionary; see oft
Dan Talayco48370102010-03-03 15:17:33 -080053 """
54
55 global basic_port_map
56 global basic_logger
57 global basic_config
58
59 basic_logger = logging.getLogger("basic")
60 basic_logger.info("Initializing test set")
61 basic_port_map = config["port_map"]
62 basic_config = config
Dan Talayco48370102010-03-03 15:17:33 -080063
Dan Talayco6ce963a2010-03-07 21:58:13 -080064class SimpleProtocol(unittest.TestCase):
Dan Talaycodba244e2010-02-15 14:08:53 -080065 """
66 Root class for setting up the controller
67 """
68
Dan Talaycoef701f42010-05-07 09:22:35 -070069 def sig_handler(self, v1, v2):
Dan Talayco48370102010-03-03 15:17:33 -080070 basic_logger.critical("Received interrupt signal; exiting")
Dan Talayco710438c2010-02-18 15:16:07 -080071 print "Received interrupt signal; exiting"
Dan Talayco2c0dba32010-03-06 22:47:06 -080072 self.clean_shutdown = False
73 self.tearDown()
Rich Lane58cf05f2012-07-11 16:41:47 -070074 raise KeyboardInterrupt
Dan Talayco710438c2010-02-18 15:16:07 -080075
Dan Talaycodba244e2010-02-15 14:08:53 -080076 def setUp(self):
Dan Talayco551befa2010-07-15 17:05:32 -070077 self.logger = basic_logger
Dan Talayco285a8382010-07-20 14:06:55 -070078 self.config = basic_config
Ed Swierk022d02e2012-08-22 06:26:36 -070079 #@todo Test cases shouldn't monkey with signals; move SIGINT handler
80 # to top-level oft
81 try:
82 signal.signal(signal.SIGINT, self.sig_handler)
83 except ValueError, e:
84 basic_logger.info("Could not set SIGINT handler: %s" % e)
Dan Talayco9f47f4d2010-06-03 13:54:37 -070085 basic_logger.info("** START TEST CASE " + str(self))
Dan Talayco2c0dba32010-03-06 22:47:06 -080086 self.controller = controller.Controller(
87 host=basic_config["controller_host"],
88 port=basic_config["controller_port"])
Dan Talaycof8f41402010-03-12 22:17:39 -080089 # clean_shutdown should be set to False to force quit app
Dan Talayco2c0dba32010-03-06 22:47:06 -080090 self.clean_shutdown = True
Dan Talayco710438c2010-02-18 15:16:07 -080091 self.controller.start()
Dan Talaycoef701f42010-05-07 09:22:35 -070092 #@todo Add an option to wait for a pkt transaction to ensure version
93 # compatibilty?
Dan Talayco710438c2010-02-18 15:16:07 -080094 self.controller.connect(timeout=20)
Dan Talayco5d0f7cc2012-09-19 11:28:58 -070095
96 # By default, respond to echo requests
97 self.controller.keep_alive = True
98
Dan Talaycoef701f42010-05-07 09:22:35 -070099 if not self.controller.active:
Rich Lane58cf05f2012-07-11 16:41:47 -0700100 raise Exception("Controller startup failed")
Dan Talayco677c0b72011-08-23 22:53:38 -0700101 if self.controller.switch_addr is None:
Rich Lane58cf05f2012-07-11 16:41:47 -0700102 raise Exception("Controller startup failed (no switch addr)")
Dan Talayco48370102010-03-03 15:17:33 -0800103 basic_logger.info("Connected " + str(self.controller.switch_addr))
Ed Swierkc7193a22012-08-22 06:51:02 -0700104 request = message.features_request()
105 reply, pkt = self.controller.transact(request, timeout=10)
Dan Talayco97d4f362012-09-18 03:22:09 -0700106 self.assertTrue(reply is not None,
107 "Did not complete features_request for handshake")
Ed Swierkc7193a22012-08-22 06:51:02 -0700108 self.supported_actions = reply.actions
109 basic_logger.info("Supported actions: " + hex(self.supported_actions))
Dan Talaycodba244e2010-02-15 14:08:53 -0800110
Dan Talayco677cc112012-03-27 10:28:58 -0700111 def inheritSetup(self, parent):
112 """
113 Inherit the setup of a parent
114
115 This allows running at test from within another test. Do the
116 following:
117
118 sub_test = SomeTestClass() # Create an instance of the test class
119 sub_test.inheritSetup(self) # Inherit setup of parent
120 sub_test.runTest() # Run the test
121
122 Normally, only the parent's setUp and tearDown are called and
123 the state after the sub_test is run must be taken into account
124 by subsequent operations.
125 """
126 self.logger = parent.logger
127 self.config = parent.config
128 basic_logger.info("** Setup " + str(self) + " inheriting from "
129 + str(parent))
130 self.controller = parent.controller
Ed Swierk6192e512012-08-22 11:41:40 -0700131 self.supported_actions = parent.supported_actions
Dan Talayco677cc112012-03-27 10:28:58 -0700132
Dan Talaycodba244e2010-02-15 14:08:53 -0800133 def tearDown(self):
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700134 basic_logger.info("** END TEST CASE " + str(self))
Dan Talaycodba244e2010-02-15 14:08:53 -0800135 self.controller.shutdown()
Dan Talayco2c0dba32010-03-06 22:47:06 -0800136 #@todo Review if join should be done on clean_shutdown
Dan Talaycof8f41402010-03-12 22:17:39 -0800137 if self.clean_shutdown:
138 self.controller.join()
Dan Talaycodba244e2010-02-15 14:08:53 -0800139
140 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -0800141 # Just a simple sanity check as illustration
Dan Talayco48370102010-03-03 15:17:33 -0800142 basic_logger.info("Running simple proto test")
Dan Talayco710438c2010-02-18 15:16:07 -0800143 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800144 str(self) + 'No connection to switch')
145
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700146 def assertTrue(self, cond, msg):
147 if not cond:
148 basic_logger.error("** FAILED ASSERTION: " + msg)
149 unittest.TestCase.assertTrue(self, cond, msg)
150
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700151test_prio["SimpleProtocol"] = 1
152
Dan Talayco6ce963a2010-03-07 21:58:13 -0800153class SimpleDataPlane(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800154 """
155 Root class that sets up the controller and dataplane
156 """
157 def setUp(self):
Dan Talayco6ce963a2010-03-07 21:58:13 -0800158 SimpleProtocol.setUp(self)
Jeffrey Townsend4d5ca922012-07-11 11:37:35 -0700159 self.dataplane = dataplane.DataPlane(self.config)
Dan Talayco48370102010-03-03 15:17:33 -0800160 for of_port, ifname in basic_port_map.items():
Dan Talaycodba244e2010-02-15 14:08:53 -0800161 self.dataplane.port_add(ifname, of_port)
162
Dan Talayco677cc112012-03-27 10:28:58 -0700163 def inheritSetup(self, parent):
164 """
165 Inherit the setup of a parent
166
167 See SimpleProtocol.inheritSetup
168 """
169 SimpleProtocol.inheritSetup(self, parent)
170 self.dataplane = parent.dataplane
171
Dan Talaycodba244e2010-02-15 14:08:53 -0800172 def tearDown(self):
Dan Talayco48370102010-03-03 15:17:33 -0800173 basic_logger.info("Teardown for simple dataplane test")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800174 SimpleProtocol.tearDown(self)
Rich Lane58cf05f2012-07-11 16:41:47 -0700175 if hasattr(self, 'dataplane'):
176 self.dataplane.kill(join_threads=self.clean_shutdown)
Dan Talayco48370102010-03-03 15:17:33 -0800177 basic_logger.info("Teardown done")
Dan Talaycodba244e2010-02-15 14:08:53 -0800178
179 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -0800180 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800181 str(self) + 'No connection to switch')
182 # self.dataplane.show()
183 # Would like an assert that checks the data plane
184
Dan Talayco551befa2010-07-15 17:05:32 -0700185class DataPlaneOnly(unittest.TestCase):
186 """
187 Root class that sets up only the dataplane
188 """
189
190 def sig_handler(self, v1, v2):
191 basic_logger.critical("Received interrupt signal; exiting")
192 print "Received interrupt signal; exiting"
193 self.clean_shutdown = False
194 self.tearDown()
Rich Lane58cf05f2012-07-11 16:41:47 -0700195 raise KeyboardInterrupt
Dan Talayco551befa2010-07-15 17:05:32 -0700196
197 def setUp(self):
Shudong Zhoue3582a52012-08-03 20:46:50 -0700198 self.clean_shutdown = True
Dan Talayco551befa2010-07-15 17:05:32 -0700199 self.logger = basic_logger
Dan Talayco285a8382010-07-20 14:06:55 -0700200 self.config = basic_config
Ed Swierk022d02e2012-08-22 06:26:36 -0700201 #@todo Test cases shouldn't monkey with signals; move SIGINT handler
202 # to top-level oft
203 try:
204 signal.signal(signal.SIGINT, self.sig_handler)
205 except ValueError, e:
206 basic_logger.info("Could not set SIGINT handler: %s" % e)
Dan Talayco551befa2010-07-15 17:05:32 -0700207 basic_logger.info("** START DataPlaneOnly CASE " + str(self))
Jeffrey Townsend4d5ca922012-07-11 11:37:35 -0700208 self.dataplane = dataplane.DataPlane(self.config)
Dan Talayco551befa2010-07-15 17:05:32 -0700209 for of_port, ifname in basic_port_map.items():
210 self.dataplane.port_add(ifname, of_port)
211
212 def tearDown(self):
213 basic_logger.info("Teardown for simple dataplane test")
214 self.dataplane.kill(join_threads=self.clean_shutdown)
215 basic_logger.info("Teardown done")
216
217 def runTest(self):
Dan Talaycoba4fd4f2010-07-21 21:49:41 -0700218 basic_logger.info("DataPlaneOnly")
Dan Talayco285a8382010-07-20 14:06:55 -0700219 # self.dataplane.show()
Dan Talayco551befa2010-07-15 17:05:32 -0700220 # Would like an assert that checks the data plane
221
Dan Talaycoa6cea8b2012-09-18 06:58:54 -0700222test_prio["DataPlaneOnly"] = -1
223
Dan Talayco6ce963a2010-03-07 21:58:13 -0800224class Echo(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800225 """
226 Test echo response with no data
227 """
228 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800229 request = message.echo_request()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800230 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700231 self.assertTrue(response is not None,
232 "Did not get echo reply")
Dan Talayco2c0dba32010-03-06 22:47:06 -0800233 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800234 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800235 self.assertEqual(request.header.xid, response.header.xid,
236 'response xid != request xid')
237 self.assertEqual(len(response.data), 0, 'response data non-empty')
238
Dan Talayco6ce963a2010-03-07 21:58:13 -0800239class EchoWithData(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800240 """
241 Test echo response with short string data
242 """
243 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800244 request = message.echo_request()
Dan Talaycodba244e2010-02-15 14:08:53 -0800245 request.data = 'OpenFlow Will Rule The World'
Dan Talaycoe226eb12010-02-18 23:06:30 -0800246 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700247 self.assertTrue(response is not None,
248 "Did not get echo reply (with data)")
Dan Talayco2c0dba32010-03-06 22:47:06 -0800249 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800250 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800251 self.assertEqual(request.header.xid, response.header.xid,
252 'response xid != request xid')
253 self.assertEqual(request.data, response.data,
254 'response data does not match request')
255
Dan Talayco6ce963a2010-03-07 21:58:13 -0800256class PacketIn(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800257 """
258 Test packet in function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800259
260 Send a packet to each dataplane port and verify that a packet
261 in message is received from the controller for each
Dan Talaycodba244e2010-02-15 14:08:53 -0800262 """
263 def runTest(self):
264 # Construct packet to send to dataplane
Dan Talaycoe226eb12010-02-18 23:06:30 -0800265 # Send packet to dataplane, once to each port
Dan Talaycodba244e2010-02-15 14:08:53 -0800266 # Poll controller with expect message type packet in
Dan Talaycoe226eb12010-02-18 23:06:30 -0800267
Dan Talayco6ce963a2010-03-07 21:58:13 -0800268 rc = delete_all_flows(self.controller, basic_logger)
269 self.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700270 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800271
Ken Chiangaeb23d62012-08-23 21:20:07 -0700272 vid = test_param_get(self.config, 'vid', default=TEST_VID_DEFAULT)
273
Dan Talayco48370102010-03-03 15:17:33 -0800274 for of_port in basic_port_map.keys():
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700275 for pkt, pt in [
276 (simple_tcp_packet(), "simple TCP packet"),
Ken Chiangaeb23d62012-08-23 21:20:07 -0700277 (simple_tcp_packet(dl_vlan_enable=True,dl_vlan=vid,pktlen=108),
278 "simple tagged TCP packet"),
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700279 (simple_eth_packet(), "simple Ethernet packet"),
280 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
Dan Talaycodba244e2010-02-15 14:08:53 -0800281
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700282 basic_logger.info("PKT IN test with %s, port %s" % (pt, of_port))
283 self.dataplane.send(of_port, str(pkt))
284 #@todo Check for unexpected messages?
285 count = 0
286 while True:
287 (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
288 if not response: # Timeout
289 break
Ed Swierk506614a2012-03-29 08:16:59 -0700290 if dataplane.match_exp_pkt(pkt, response.data): # Got match
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700291 break
292 if not basic_config["relax"]: # Only one attempt to match
293 break
294 count += 1
295 if count > 10: # Too many tries
296 break
Dan Talayco48370102010-03-03 15:17:33 -0800297
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700298 self.assertTrue(response is not None,
299 'Packet in message not received on port ' +
300 str(of_port))
Ed Swierk506614a2012-03-29 08:16:59 -0700301 if not dataplane.match_exp_pkt(pkt, response.data):
Dan Talayco2baf8b52012-03-30 09:55:42 -0700302 basic_logger.debug("Sent %s" % format_packet(pkt))
303 basic_logger.debug("Resp %s" % format_packet(response.data))
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700304 self.assertTrue(False,
305 'Response packet does not match send packet' +
306 ' for port ' + str(of_port))
Dan Talaycodba244e2010-02-15 14:08:53 -0800307
Ed Swierk3ae7f712012-08-22 06:45:25 -0700308class PacketInDefaultDrop(SimpleDataPlane):
309 """
310 Test packet in function
311
312 Send a packet to each dataplane port and verify that a packet
313 in message is received from the controller for each
314 """
315 def runTest(self):
316 rc = delete_all_flows(self.controller, basic_logger)
317 self.assertEqual(rc, 0, "Failed to delete all flows")
318 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
319
320 for of_port in basic_port_map.keys():
321 pkt = simple_tcp_packet()
322 self.dataplane.send(of_port, str(pkt))
323 count = 0
324 while True:
325 (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
326 if not response: # Timeout
327 break
328 if dataplane.match_exp_pkt(pkt, response.data): # Got match
329 break
330 if not basic_config["relax"]: # Only one attempt to match
331 break
332 count += 1
333 if count > 10: # Too many tries
334 break
335
336 self.assertTrue(response is None,
337 'Packet in message received on port ' +
338 str(of_port))
339
340test_prio["PacketInDefaultDrop"] = -1
341
Jeffrey Townsend4d5ca922012-07-11 11:37:35 -0700342
Dan Talayco1f648cb2012-05-03 09:37:56 -0700343class PacketInBroadcastCheck(SimpleDataPlane):
344 """
345 Check if bcast pkts leak when no flows are present
346
347 Clear the flow table
348 Send in a broadcast pkt
349 Look for the packet on other dataplane ports.
350 """
351 def runTest(self):
352 # Need at least two ports
353 self.assertTrue(len(basic_port_map) > 1, "Too few ports for test")
354
355 rc = delete_all_flows(self.controller, basic_logger)
356 self.assertEqual(rc, 0, "Failed to delete all flows")
357 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
358
359 of_ports = basic_port_map.keys()
360 d_port = of_ports[0]
361 pkt = simple_eth_packet(dl_dst='ff:ff:ff:ff:ff:ff')
362
363 basic_logger.info("BCast Leak Test, send to port %s" % d_port)
364 self.dataplane.send(d_port, str(pkt))
365
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700366 (of_port, pkt_in, pkt_time) = self.dataplane.poll(exp_pkt=pkt)
Dan Talayco1f648cb2012-05-03 09:37:56 -0700367 self.assertTrue(pkt_in is None,
368 'BCast packet received on port ' + str(of_port))
369
370test_prio["PacketInBroadcastCheck"] = -1
371
Dan Talayco6ce963a2010-03-07 21:58:13 -0800372class PacketOut(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800373 """
374 Test packet out function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800375
376 Send packet out message to controller for each dataplane port and
377 verify the packet appears on the appropriate dataplane port
Dan Talaycodba244e2010-02-15 14:08:53 -0800378 """
379 def runTest(self):
380 # Construct packet to send to dataplane
381 # Send packet to dataplane
382 # Poll controller with expect message type packet in
Dan Talayco41eae8b2010-03-10 13:57:06 -0800383
384 rc = delete_all_flows(self.controller, basic_logger)
385 self.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talaycodba244e2010-02-15 14:08:53 -0800386
387 # These will get put into function
Dan Talayco48370102010-03-03 15:17:33 -0800388 of_ports = basic_port_map.keys()
389 of_ports.sort()
390 for dp_port in of_ports:
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700391 for outpkt, opt in [
392 (simple_tcp_packet(), "simple TCP packet"),
393 (simple_eth_packet(), "simple Ethernet packet"),
394 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
Dan Talaycodba244e2010-02-15 14:08:53 -0800395
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700396 basic_logger.info("PKT OUT test with %s, port %s" % (opt, dp_port))
397 msg = message.packet_out()
398 msg.data = str(outpkt)
399 act = action.action_output()
400 act.port = dp_port
401 self.assertTrue(msg.actions.add(act), 'Could not add action to msg')
Dan Talaycodba244e2010-02-15 14:08:53 -0800402
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700403 basic_logger.info("PacketOut to: " + str(dp_port))
404 rv = self.controller.message_send(msg)
405 self.assertTrue(rv == 0, "Error sending out message")
Dan Talaycodba244e2010-02-15 14:08:53 -0800406
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700407 exp_pkt_arg = None
408 exp_port = None
409 if basic_config["relax"]:
410 exp_pkt_arg = outpkt
411 exp_port = dp_port
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700412 (of_port, pkt, pkt_time) = self.dataplane.poll(port_number=exp_port,
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700413 exp_pkt=exp_pkt_arg)
414
415 self.assertTrue(pkt is not None, 'Packet not received')
416 basic_logger.info("PacketOut: got pkt from " + str(of_port))
417 if of_port is not None:
418 self.assertEqual(of_port, dp_port, "Unexpected receive port")
Ed Swierk506614a2012-03-29 08:16:59 -0700419 if not dataplane.match_exp_pkt(outpkt, pkt):
Dan Talayco2baf8b52012-03-30 09:55:42 -0700420 basic_logger.debug("Sent %s" % format_packet(outpkt))
421 basic_logger.debug("Resp %s" % format_packet(
422 str(pkt)[:len(str(outpkt))]))
Dan Talaycodc6fca32012-03-30 10:05:49 -0700423 self.assertEqual(str(outpkt), str(pkt)[:len(str(outpkt))],
424 'Response packet does not match send packet')
Dan Talaycodba244e2010-02-15 14:08:53 -0800425
Ken Chiang1bf01602012-04-04 10:48:23 -0700426class PacketOutMC(SimpleDataPlane):
427 """
428 Test packet out to multiple output ports
429
430 Send packet out message to controller for 1 to N dataplane ports and
431 verify the packet appears on the appropriate ports
432 """
433 def runTest(self):
434 # Construct packet to send to dataplane
435 # Send packet to dataplane
436 # Poll controller with expect message type packet in
437
438 rc = delete_all_flows(self.controller, basic_logger)
439 self.assertEqual(rc, 0, "Failed to delete all flows")
440
441 # These will get put into function
442 of_ports = basic_port_map.keys()
443 random.shuffle(of_ports)
444 for num_ports in range(1,len(of_ports)+1):
445 for outpkt, opt in [
446 (simple_tcp_packet(), "simple TCP packet"),
447 (simple_eth_packet(), "simple Ethernet packet"),
448 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
449
450 dp_ports = of_ports[0:num_ports]
451 basic_logger.info("PKT OUT test with " + opt +
452 ", ports " + str(dp_ports))
453 msg = message.packet_out()
454 msg.data = str(outpkt)
455 act = action.action_output()
456 for i in range(0,num_ports):
457 act.port = dp_ports[i]
458 self.assertTrue(msg.actions.add(act),
459 'Could not add action to msg')
460
461 basic_logger.info("PacketOut to: " + str(dp_ports))
462 rv = self.controller.message_send(msg)
463 self.assertTrue(rv == 0, "Error sending out message")
464
465 receive_pkt_check(self.dataplane, outpkt, dp_ports,
466 set(of_ports).difference(dp_ports),
467 self, basic_logger, basic_config)
468
Dan Talayco6ce963a2010-03-07 21:58:13 -0800469class FlowStatsGet(SimpleProtocol):
470 """
471 Get stats
Dan Talayco2c0dba32010-03-06 22:47:06 -0800472
Dan Talayco6ce963a2010-03-07 21:58:13 -0800473 Simply verify stats get transaction
474 """
475 def runTest(self):
476 basic_logger.info("Running StatsGet")
Dan Talayco41eae8b2010-03-10 13:57:06 -0800477 basic_logger.info("Inserting trial flow")
Dan Talayco677c0b72011-08-23 22:53:38 -0700478 request = flow_mod_gen(basic_port_map, True)
Dan Talayco41eae8b2010-03-10 13:57:06 -0800479 rv = self.controller.message_send(request)
480 self.assertTrue(rv != -1, "Failed to insert test flow")
481
482 basic_logger.info("Sending flow request")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800483 request = message.flow_stats_request()
484 request.out_port = ofp.OFPP_NONE
Dan Talayco41eae8b2010-03-10 13:57:06 -0800485 request.table_id = 0xff
486 request.match.wildcards = 0 # ofp.OFPFW_ALL
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700487 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700488 self.assertTrue(response is not None,
489 "Did not get response for flow stats")
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700490 basic_logger.debug(response.show())
Dan Talayco6ce963a2010-03-07 21:58:13 -0800491
Dan Talayco677c0b72011-08-23 22:53:38 -0700492test_prio["FlowStatsGet"] = -1
493
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700494class TableStatsGet(SimpleProtocol):
495 """
496 Get table stats
497
498 Simply verify table stats get transaction
499 """
500 def runTest(self):
501 basic_logger.info("Running TableStatsGet")
502 basic_logger.info("Inserting trial flow")
Dan Talayco677c0b72011-08-23 22:53:38 -0700503 request = flow_mod_gen(basic_port_map, True)
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700504 rv = self.controller.message_send(request)
505 self.assertTrue(rv != -1, "Failed to insert test flow")
506
507 basic_logger.info("Sending table stats request")
508 request = message.table_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700509 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700510 self.assertTrue(response is not None,
511 "Did not get reply for table stats")
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700512 basic_logger.debug(response.show())
513
Ed Swierkae74c362012-04-02 08:21:41 -0700514class DescStatsGet(SimpleProtocol):
515 """
516 Get stats
517
518 Simply verify stats get transaction
519 """
520 def runTest(self):
521 basic_logger.info("Running DescStatsGet")
522
523 basic_logger.info("Sending stats request")
524 request = message.desc_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700525 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700526 self.assertTrue(response is not None,
527 "Did not get reply for desc stats")
Ed Swierkae74c362012-04-02 08:21:41 -0700528 basic_logger.debug(response.show())
529
Dan Talayco6ce963a2010-03-07 21:58:13 -0800530class FlowMod(SimpleProtocol):
531 """
532 Insert a flow
533
534 Simple verification of a flow mod transaction
535 """
536
537 def runTest(self):
538 basic_logger.info("Running " + str(self))
Dan Talayco677c0b72011-08-23 22:53:38 -0700539 request = flow_mod_gen(basic_port_map, True)
Dan Talayco6ce963a2010-03-07 21:58:13 -0800540 rv = self.controller.message_send(request)
Dan Talayco41eae8b2010-03-10 13:57:06 -0800541 self.assertTrue(rv != -1, "Error installing flow mod")
542
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700543class PortConfigMod(SimpleProtocol):
544 """
545 Modify a bit in port config and verify changed
546
547 Get the switch configuration, modify the port configuration
548 and write it back; get the config again and verify changed.
549 Then set it back to the way it was.
550 """
551
552 def runTest(self):
553 basic_logger.info("Running " + str(self))
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700554 for of_port, ifname in basic_port_map.items(): # Grab first port
555 break
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700556
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700557 (hw_addr, config, advert) = \
558 port_config_get(self.controller, of_port, basic_logger)
559 self.assertTrue(config is not None, "Did not get port config")
560
561 basic_logger.debug("No flood bit port " + str(of_port) + " is now " +
562 str(config & ofp.OFPPC_NO_FLOOD))
563
564 rv = port_config_set(self.controller, of_port,
565 config ^ ofp.OFPPC_NO_FLOOD, ofp.OFPPC_NO_FLOOD,
566 basic_logger)
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700567 self.assertTrue(rv != -1, "Error sending port mod")
568
569 # Verify change took place with same feature request
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700570 (hw_addr, config2, advert) = \
571 port_config_get(self.controller, of_port, basic_logger)
572 basic_logger.debug("No flood bit port " + str(of_port) + " is now " +
573 str(config2 & ofp.OFPPC_NO_FLOOD))
574 self.assertTrue(config2 is not None, "Did not get port config2")
575 self.assertTrue(config2 & ofp.OFPPC_NO_FLOOD !=
576 config & ofp.OFPPC_NO_FLOOD,
577 "Bit change did not take")
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700578 # Set it back
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700579 rv = port_config_set(self.controller, of_port, config,
580 ofp.OFPPC_NO_FLOOD, basic_logger)
581 self.assertTrue(rv != -1, "Error sending port mod")
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700582
Ken Chiangaeb23d62012-08-23 21:20:07 -0700583class PortConfigModErr(SimpleProtocol):
584 """
585 Modify a bit in port config on an invalid port and verify
586 error message is received.
587 """
588
589 def runTest(self):
590 basic_logger.info("Running " + str(self))
591
592 # pick a random bad port number
593 bad_port = random.randint(1, ofp.OFPP_MAX)
594 count = 0
595 while (count < 50) and (bad_port in basic_port_map.keys()):
596 bad_port = random.randint(1, ofp.OFPP_MAX)
597 count = count + 1
598 self.assertTrue(count < 50, "Error selecting bad port")
599 basic_logger.info("Select " + str(bad_port) + " as invalid port")
600
601 rv = port_config_set(self.controller, bad_port,
602 ofp.OFPPC_NO_FLOOD, ofp.OFPPC_NO_FLOOD,
603 basic_logger)
604 self.assertTrue(rv != -1, "Error sending port mod")
605
606 # poll for error message
607 while True:
608 (response, raw) = self.controller.poll(ofp.OFPT_ERROR, 2)
609 if not response: # Timeout
610 break
611 if response.code == ofp.OFPPMFC_BAD_PORT:
612 basic_logger.info("Received error message with OFPPMFC_BAD_PORT code")
613 break
614 if not basic_config["relax"]: # Only one attempt to match
615 break
616 count += 1
617 if count > 10: # Too many tries
618 break
619
620 self.assertTrue(response is not None, 'Did not receive error message')
621
Dan Talaycoe605b1b2012-09-18 06:56:20 -0700622class BadMessage(SimpleProtocol):
623 """
624 Send a message with a bad type and verify an error is returned
625 """
626
627 def runTest(self):
628 basic_logger.info("Running " + str(self))
629 request = illegal_message.illegal_message_type()
630
631 reply, pkt = self.controller.transact(request, timeout=10)
632 self.assertTrue(reply is not None, "Did not get response to bad req")
633 self.assertTrue(reply.header.type == ofp.OFPT_ERROR,
634 "reply not an error message")
635 self.assertTrue(reply.type == ofp.OFPET_BAD_REQUEST,
636 "reply error type is not bad request")
637 self.assertTrue(reply.code == ofp.OFPBRC_BAD_TYPE,
638 "reply error code is not bad type")
639
Dan Talaycodba244e2010-02-15 14:08:53 -0800640if __name__ == "__main__":
Dan Talayco2c0dba32010-03-06 22:47:06 -0800641 print "Please run through oft script: ./oft --test_spec=basic"