blob: 0d90273f9e56a8e7751d6fc48d53c049c61c3e08 [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 Talaycoef701f42010-05-07 09:22:35 -070095 if not self.controller.active:
Rich Lane58cf05f2012-07-11 16:41:47 -070096 raise Exception("Controller startup failed")
Dan Talayco677c0b72011-08-23 22:53:38 -070097 if self.controller.switch_addr is None:
Rich Lane58cf05f2012-07-11 16:41:47 -070098 raise Exception("Controller startup failed (no switch addr)")
Dan Talayco48370102010-03-03 15:17:33 -080099 basic_logger.info("Connected " + str(self.controller.switch_addr))
Ed Swierkc7193a22012-08-22 06:51:02 -0700100 request = message.features_request()
101 reply, pkt = self.controller.transact(request, timeout=10)
Dan Talayco97d4f362012-09-18 03:22:09 -0700102 self.assertTrue(reply is not None,
103 "Did not complete features_request for handshake")
Ed Swierkc7193a22012-08-22 06:51:02 -0700104 self.supported_actions = reply.actions
105 basic_logger.info("Supported actions: " + hex(self.supported_actions))
Dan Talaycodba244e2010-02-15 14:08:53 -0800106
Dan Talayco677cc112012-03-27 10:28:58 -0700107 def inheritSetup(self, parent):
108 """
109 Inherit the setup of a parent
110
111 This allows running at test from within another test. Do the
112 following:
113
114 sub_test = SomeTestClass() # Create an instance of the test class
115 sub_test.inheritSetup(self) # Inherit setup of parent
116 sub_test.runTest() # Run the test
117
118 Normally, only the parent's setUp and tearDown are called and
119 the state after the sub_test is run must be taken into account
120 by subsequent operations.
121 """
122 self.logger = parent.logger
123 self.config = parent.config
124 basic_logger.info("** Setup " + str(self) + " inheriting from "
125 + str(parent))
126 self.controller = parent.controller
Ed Swierk6192e512012-08-22 11:41:40 -0700127 self.supported_actions = parent.supported_actions
Dan Talayco677cc112012-03-27 10:28:58 -0700128
Dan Talaycodba244e2010-02-15 14:08:53 -0800129 def tearDown(self):
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700130 basic_logger.info("** END TEST CASE " + str(self))
Dan Talaycodba244e2010-02-15 14:08:53 -0800131 self.controller.shutdown()
Dan Talayco2c0dba32010-03-06 22:47:06 -0800132 #@todo Review if join should be done on clean_shutdown
Dan Talaycof8f41402010-03-12 22:17:39 -0800133 if self.clean_shutdown:
134 self.controller.join()
Dan Talaycodba244e2010-02-15 14:08:53 -0800135
136 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -0800137 # Just a simple sanity check as illustration
Dan Talayco48370102010-03-03 15:17:33 -0800138 basic_logger.info("Running simple proto test")
Dan Talayco710438c2010-02-18 15:16:07 -0800139 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800140 str(self) + 'No connection to switch')
141
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700142 def assertTrue(self, cond, msg):
143 if not cond:
144 basic_logger.error("** FAILED ASSERTION: " + msg)
145 unittest.TestCase.assertTrue(self, cond, msg)
146
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700147test_prio["SimpleProtocol"] = 1
148
Dan Talayco6ce963a2010-03-07 21:58:13 -0800149class SimpleDataPlane(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800150 """
151 Root class that sets up the controller and dataplane
152 """
153 def setUp(self):
Dan Talayco6ce963a2010-03-07 21:58:13 -0800154 SimpleProtocol.setUp(self)
Jeffrey Townsend4d5ca922012-07-11 11:37:35 -0700155 self.dataplane = dataplane.DataPlane(self.config)
Dan Talayco48370102010-03-03 15:17:33 -0800156 for of_port, ifname in basic_port_map.items():
Dan Talaycodba244e2010-02-15 14:08:53 -0800157 self.dataplane.port_add(ifname, of_port)
158
Dan Talayco677cc112012-03-27 10:28:58 -0700159 def inheritSetup(self, parent):
160 """
161 Inherit the setup of a parent
162
163 See SimpleProtocol.inheritSetup
164 """
165 SimpleProtocol.inheritSetup(self, parent)
166 self.dataplane = parent.dataplane
167
Dan Talaycodba244e2010-02-15 14:08:53 -0800168 def tearDown(self):
Dan Talayco48370102010-03-03 15:17:33 -0800169 basic_logger.info("Teardown for simple dataplane test")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800170 SimpleProtocol.tearDown(self)
Rich Lane58cf05f2012-07-11 16:41:47 -0700171 if hasattr(self, 'dataplane'):
172 self.dataplane.kill(join_threads=self.clean_shutdown)
Dan Talayco48370102010-03-03 15:17:33 -0800173 basic_logger.info("Teardown done")
Dan Talaycodba244e2010-02-15 14:08:53 -0800174
175 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -0800176 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800177 str(self) + 'No connection to switch')
178 # self.dataplane.show()
179 # Would like an assert that checks the data plane
180
Dan Talayco551befa2010-07-15 17:05:32 -0700181class DataPlaneOnly(unittest.TestCase):
182 """
183 Root class that sets up only the dataplane
184 """
185
186 def sig_handler(self, v1, v2):
187 basic_logger.critical("Received interrupt signal; exiting")
188 print "Received interrupt signal; exiting"
189 self.clean_shutdown = False
190 self.tearDown()
Rich Lane58cf05f2012-07-11 16:41:47 -0700191 raise KeyboardInterrupt
Dan Talayco551befa2010-07-15 17:05:32 -0700192
193 def setUp(self):
Shudong Zhoue3582a52012-08-03 20:46:50 -0700194 self.clean_shutdown = True
Dan Talayco551befa2010-07-15 17:05:32 -0700195 self.logger = basic_logger
Dan Talayco285a8382010-07-20 14:06:55 -0700196 self.config = basic_config
Ed Swierk022d02e2012-08-22 06:26:36 -0700197 #@todo Test cases shouldn't monkey with signals; move SIGINT handler
198 # to top-level oft
199 try:
200 signal.signal(signal.SIGINT, self.sig_handler)
201 except ValueError, e:
202 basic_logger.info("Could not set SIGINT handler: %s" % e)
Dan Talayco551befa2010-07-15 17:05:32 -0700203 basic_logger.info("** START DataPlaneOnly CASE " + str(self))
Jeffrey Townsend4d5ca922012-07-11 11:37:35 -0700204 self.dataplane = dataplane.DataPlane(self.config)
Dan Talayco551befa2010-07-15 17:05:32 -0700205 for of_port, ifname in basic_port_map.items():
206 self.dataplane.port_add(ifname, of_port)
207
208 def tearDown(self):
209 basic_logger.info("Teardown for simple dataplane test")
210 self.dataplane.kill(join_threads=self.clean_shutdown)
211 basic_logger.info("Teardown done")
212
213 def runTest(self):
Dan Talaycoba4fd4f2010-07-21 21:49:41 -0700214 basic_logger.info("DataPlaneOnly")
Dan Talayco285a8382010-07-20 14:06:55 -0700215 # self.dataplane.show()
Dan Talayco551befa2010-07-15 17:05:32 -0700216 # Would like an assert that checks the data plane
217
Dan Talaycoa6cea8b2012-09-18 06:58:54 -0700218test_prio["DataPlaneOnly"] = -1
219
Dan Talayco6ce963a2010-03-07 21:58:13 -0800220class Echo(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800221 """
222 Test echo response with no data
223 """
224 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800225 request = message.echo_request()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800226 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700227 self.assertTrue(response is not None,
228 "Did not get echo reply")
Dan Talayco2c0dba32010-03-06 22:47:06 -0800229 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800230 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800231 self.assertEqual(request.header.xid, response.header.xid,
232 'response xid != request xid')
233 self.assertEqual(len(response.data), 0, 'response data non-empty')
234
Dan Talayco6ce963a2010-03-07 21:58:13 -0800235class EchoWithData(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800236 """
237 Test echo response with short string data
238 """
239 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800240 request = message.echo_request()
Dan Talaycodba244e2010-02-15 14:08:53 -0800241 request.data = 'OpenFlow Will Rule The World'
Dan Talaycoe226eb12010-02-18 23:06:30 -0800242 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700243 self.assertTrue(response is not None,
244 "Did not get echo reply (with data)")
Dan Talayco2c0dba32010-03-06 22:47:06 -0800245 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800246 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800247 self.assertEqual(request.header.xid, response.header.xid,
248 'response xid != request xid')
249 self.assertEqual(request.data, response.data,
250 'response data does not match request')
251
Dan Talayco6ce963a2010-03-07 21:58:13 -0800252class PacketIn(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800253 """
254 Test packet in function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800255
256 Send a packet to each dataplane port and verify that a packet
257 in message is received from the controller for each
Dan Talaycodba244e2010-02-15 14:08:53 -0800258 """
259 def runTest(self):
260 # Construct packet to send to dataplane
Dan Talaycoe226eb12010-02-18 23:06:30 -0800261 # Send packet to dataplane, once to each port
Dan Talaycodba244e2010-02-15 14:08:53 -0800262 # Poll controller with expect message type packet in
Dan Talaycoe226eb12010-02-18 23:06:30 -0800263
Dan Talayco6ce963a2010-03-07 21:58:13 -0800264 rc = delete_all_flows(self.controller, basic_logger)
265 self.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700266 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800267
Ken Chiangaeb23d62012-08-23 21:20:07 -0700268 vid = test_param_get(self.config, 'vid', default=TEST_VID_DEFAULT)
269
Dan Talayco48370102010-03-03 15:17:33 -0800270 for of_port in basic_port_map.keys():
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700271 for pkt, pt in [
272 (simple_tcp_packet(), "simple TCP packet"),
Ken Chiangaeb23d62012-08-23 21:20:07 -0700273 (simple_tcp_packet(dl_vlan_enable=True,dl_vlan=vid,pktlen=108),
274 "simple tagged TCP packet"),
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700275 (simple_eth_packet(), "simple Ethernet packet"),
276 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
Dan Talaycodba244e2010-02-15 14:08:53 -0800277
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700278 basic_logger.info("PKT IN test with %s, port %s" % (pt, of_port))
279 self.dataplane.send(of_port, str(pkt))
280 #@todo Check for unexpected messages?
281 count = 0
282 while True:
283 (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
284 if not response: # Timeout
285 break
Ed Swierk506614a2012-03-29 08:16:59 -0700286 if dataplane.match_exp_pkt(pkt, response.data): # Got match
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700287 break
288 if not basic_config["relax"]: # Only one attempt to match
289 break
290 count += 1
291 if count > 10: # Too many tries
292 break
Dan Talayco48370102010-03-03 15:17:33 -0800293
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700294 self.assertTrue(response is not None,
295 'Packet in message not received on port ' +
296 str(of_port))
Ed Swierk506614a2012-03-29 08:16:59 -0700297 if not dataplane.match_exp_pkt(pkt, response.data):
Dan Talayco2baf8b52012-03-30 09:55:42 -0700298 basic_logger.debug("Sent %s" % format_packet(pkt))
299 basic_logger.debug("Resp %s" % format_packet(response.data))
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700300 self.assertTrue(False,
301 'Response packet does not match send packet' +
302 ' for port ' + str(of_port))
Dan Talaycodba244e2010-02-15 14:08:53 -0800303
Ed Swierk3ae7f712012-08-22 06:45:25 -0700304class PacketInDefaultDrop(SimpleDataPlane):
305 """
306 Test packet in function
307
308 Send a packet to each dataplane port and verify that a packet
309 in message is received from the controller for each
310 """
311 def runTest(self):
312 rc = delete_all_flows(self.controller, basic_logger)
313 self.assertEqual(rc, 0, "Failed to delete all flows")
314 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
315
316 for of_port in basic_port_map.keys():
317 pkt = simple_tcp_packet()
318 self.dataplane.send(of_port, str(pkt))
319 count = 0
320 while True:
321 (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
322 if not response: # Timeout
323 break
324 if dataplane.match_exp_pkt(pkt, response.data): # Got match
325 break
326 if not basic_config["relax"]: # Only one attempt to match
327 break
328 count += 1
329 if count > 10: # Too many tries
330 break
331
332 self.assertTrue(response is None,
333 'Packet in message received on port ' +
334 str(of_port))
335
336test_prio["PacketInDefaultDrop"] = -1
337
Jeffrey Townsend4d5ca922012-07-11 11:37:35 -0700338
Dan Talayco1f648cb2012-05-03 09:37:56 -0700339class PacketInBroadcastCheck(SimpleDataPlane):
340 """
341 Check if bcast pkts leak when no flows are present
342
343 Clear the flow table
344 Send in a broadcast pkt
345 Look for the packet on other dataplane ports.
346 """
347 def runTest(self):
348 # Need at least two ports
349 self.assertTrue(len(basic_port_map) > 1, "Too few ports for test")
350
351 rc = delete_all_flows(self.controller, basic_logger)
352 self.assertEqual(rc, 0, "Failed to delete all flows")
353 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
354
355 of_ports = basic_port_map.keys()
356 d_port = of_ports[0]
357 pkt = simple_eth_packet(dl_dst='ff:ff:ff:ff:ff:ff')
358
359 basic_logger.info("BCast Leak Test, send to port %s" % d_port)
360 self.dataplane.send(d_port, str(pkt))
361
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700362 (of_port, pkt_in, pkt_time) = self.dataplane.poll(exp_pkt=pkt)
Dan Talayco1f648cb2012-05-03 09:37:56 -0700363 self.assertTrue(pkt_in is None,
364 'BCast packet received on port ' + str(of_port))
365
366test_prio["PacketInBroadcastCheck"] = -1
367
Dan Talayco6ce963a2010-03-07 21:58:13 -0800368class PacketOut(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800369 """
370 Test packet out function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800371
372 Send packet out message to controller for each dataplane port and
373 verify the packet appears on the appropriate dataplane port
Dan Talaycodba244e2010-02-15 14:08:53 -0800374 """
375 def runTest(self):
376 # Construct packet to send to dataplane
377 # Send packet to dataplane
378 # Poll controller with expect message type packet in
Dan Talayco41eae8b2010-03-10 13:57:06 -0800379
380 rc = delete_all_flows(self.controller, basic_logger)
381 self.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talaycodba244e2010-02-15 14:08:53 -0800382
383 # These will get put into function
Dan Talayco48370102010-03-03 15:17:33 -0800384 of_ports = basic_port_map.keys()
385 of_ports.sort()
386 for dp_port in of_ports:
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700387 for outpkt, opt in [
388 (simple_tcp_packet(), "simple TCP packet"),
389 (simple_eth_packet(), "simple Ethernet packet"),
390 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
Dan Talaycodba244e2010-02-15 14:08:53 -0800391
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700392 basic_logger.info("PKT OUT test with %s, port %s" % (opt, dp_port))
393 msg = message.packet_out()
394 msg.data = str(outpkt)
395 act = action.action_output()
396 act.port = dp_port
397 self.assertTrue(msg.actions.add(act), 'Could not add action to msg')
Dan Talaycodba244e2010-02-15 14:08:53 -0800398
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700399 basic_logger.info("PacketOut to: " + str(dp_port))
400 rv = self.controller.message_send(msg)
401 self.assertTrue(rv == 0, "Error sending out message")
Dan Talaycodba244e2010-02-15 14:08:53 -0800402
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700403 exp_pkt_arg = None
404 exp_port = None
405 if basic_config["relax"]:
406 exp_pkt_arg = outpkt
407 exp_port = dp_port
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700408 (of_port, pkt, pkt_time) = self.dataplane.poll(port_number=exp_port,
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700409 exp_pkt=exp_pkt_arg)
410
411 self.assertTrue(pkt is not None, 'Packet not received')
412 basic_logger.info("PacketOut: got pkt from " + str(of_port))
413 if of_port is not None:
414 self.assertEqual(of_port, dp_port, "Unexpected receive port")
Ed Swierk506614a2012-03-29 08:16:59 -0700415 if not dataplane.match_exp_pkt(outpkt, pkt):
Dan Talayco2baf8b52012-03-30 09:55:42 -0700416 basic_logger.debug("Sent %s" % format_packet(outpkt))
417 basic_logger.debug("Resp %s" % format_packet(
418 str(pkt)[:len(str(outpkt))]))
Dan Talaycodc6fca32012-03-30 10:05:49 -0700419 self.assertEqual(str(outpkt), str(pkt)[:len(str(outpkt))],
420 'Response packet does not match send packet')
Dan Talaycodba244e2010-02-15 14:08:53 -0800421
Ken Chiang1bf01602012-04-04 10:48:23 -0700422class PacketOutMC(SimpleDataPlane):
423 """
424 Test packet out to multiple output ports
425
426 Send packet out message to controller for 1 to N dataplane ports and
427 verify the packet appears on the appropriate ports
428 """
429 def runTest(self):
430 # Construct packet to send to dataplane
431 # Send packet to dataplane
432 # Poll controller with expect message type packet in
433
434 rc = delete_all_flows(self.controller, basic_logger)
435 self.assertEqual(rc, 0, "Failed to delete all flows")
436
437 # These will get put into function
438 of_ports = basic_port_map.keys()
439 random.shuffle(of_ports)
440 for num_ports in range(1,len(of_ports)+1):
441 for outpkt, opt in [
442 (simple_tcp_packet(), "simple TCP packet"),
443 (simple_eth_packet(), "simple Ethernet packet"),
444 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
445
446 dp_ports = of_ports[0:num_ports]
447 basic_logger.info("PKT OUT test with " + opt +
448 ", ports " + str(dp_ports))
449 msg = message.packet_out()
450 msg.data = str(outpkt)
451 act = action.action_output()
452 for i in range(0,num_ports):
453 act.port = dp_ports[i]
454 self.assertTrue(msg.actions.add(act),
455 'Could not add action to msg')
456
457 basic_logger.info("PacketOut to: " + str(dp_ports))
458 rv = self.controller.message_send(msg)
459 self.assertTrue(rv == 0, "Error sending out message")
460
461 receive_pkt_check(self.dataplane, outpkt, dp_ports,
462 set(of_ports).difference(dp_ports),
463 self, basic_logger, basic_config)
464
Dan Talayco6ce963a2010-03-07 21:58:13 -0800465class FlowStatsGet(SimpleProtocol):
466 """
467 Get stats
Dan Talayco2c0dba32010-03-06 22:47:06 -0800468
Dan Talayco6ce963a2010-03-07 21:58:13 -0800469 Simply verify stats get transaction
470 """
471 def runTest(self):
472 basic_logger.info("Running StatsGet")
Dan Talayco41eae8b2010-03-10 13:57:06 -0800473 basic_logger.info("Inserting trial flow")
Dan Talayco677c0b72011-08-23 22:53:38 -0700474 request = flow_mod_gen(basic_port_map, True)
Dan Talayco41eae8b2010-03-10 13:57:06 -0800475 rv = self.controller.message_send(request)
476 self.assertTrue(rv != -1, "Failed to insert test flow")
477
478 basic_logger.info("Sending flow request")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800479 request = message.flow_stats_request()
480 request.out_port = ofp.OFPP_NONE
Dan Talayco41eae8b2010-03-10 13:57:06 -0800481 request.table_id = 0xff
482 request.match.wildcards = 0 # ofp.OFPFW_ALL
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700483 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700484 self.assertTrue(response is not None,
485 "Did not get response for flow stats")
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700486 basic_logger.debug(response.show())
Dan Talayco6ce963a2010-03-07 21:58:13 -0800487
Dan Talayco677c0b72011-08-23 22:53:38 -0700488test_prio["FlowStatsGet"] = -1
489
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700490class TableStatsGet(SimpleProtocol):
491 """
492 Get table stats
493
494 Simply verify table stats get transaction
495 """
496 def runTest(self):
497 basic_logger.info("Running TableStatsGet")
498 basic_logger.info("Inserting trial flow")
Dan Talayco677c0b72011-08-23 22:53:38 -0700499 request = flow_mod_gen(basic_port_map, True)
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700500 rv = self.controller.message_send(request)
501 self.assertTrue(rv != -1, "Failed to insert test flow")
502
503 basic_logger.info("Sending table stats request")
504 request = message.table_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700505 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700506 self.assertTrue(response is not None,
507 "Did not get reply for table stats")
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700508 basic_logger.debug(response.show())
509
Ed Swierkae74c362012-04-02 08:21:41 -0700510class DescStatsGet(SimpleProtocol):
511 """
512 Get stats
513
514 Simply verify stats get transaction
515 """
516 def runTest(self):
517 basic_logger.info("Running DescStatsGet")
518
519 basic_logger.info("Sending stats request")
520 request = message.desc_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700521 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700522 self.assertTrue(response is not None,
523 "Did not get reply for desc stats")
Ed Swierkae74c362012-04-02 08:21:41 -0700524 basic_logger.debug(response.show())
525
Dan Talayco6ce963a2010-03-07 21:58:13 -0800526class FlowMod(SimpleProtocol):
527 """
528 Insert a flow
529
530 Simple verification of a flow mod transaction
531 """
532
533 def runTest(self):
534 basic_logger.info("Running " + str(self))
Dan Talayco677c0b72011-08-23 22:53:38 -0700535 request = flow_mod_gen(basic_port_map, True)
Dan Talayco6ce963a2010-03-07 21:58:13 -0800536 rv = self.controller.message_send(request)
Dan Talayco41eae8b2010-03-10 13:57:06 -0800537 self.assertTrue(rv != -1, "Error installing flow mod")
538
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700539class PortConfigMod(SimpleProtocol):
540 """
541 Modify a bit in port config and verify changed
542
543 Get the switch configuration, modify the port configuration
544 and write it back; get the config again and verify changed.
545 Then set it back to the way it was.
546 """
547
548 def runTest(self):
549 basic_logger.info("Running " + str(self))
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700550 for of_port, ifname in basic_port_map.items(): # Grab first port
551 break
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700552
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700553 (hw_addr, config, advert) = \
554 port_config_get(self.controller, of_port, basic_logger)
555 self.assertTrue(config is not None, "Did not get port config")
556
557 basic_logger.debug("No flood bit port " + str(of_port) + " is now " +
558 str(config & ofp.OFPPC_NO_FLOOD))
559
560 rv = port_config_set(self.controller, of_port,
561 config ^ ofp.OFPPC_NO_FLOOD, ofp.OFPPC_NO_FLOOD,
562 basic_logger)
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700563 self.assertTrue(rv != -1, "Error sending port mod")
564
565 # Verify change took place with same feature request
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700566 (hw_addr, config2, advert) = \
567 port_config_get(self.controller, of_port, basic_logger)
568 basic_logger.debug("No flood bit port " + str(of_port) + " is now " +
569 str(config2 & ofp.OFPPC_NO_FLOOD))
570 self.assertTrue(config2 is not None, "Did not get port config2")
571 self.assertTrue(config2 & ofp.OFPPC_NO_FLOOD !=
572 config & ofp.OFPPC_NO_FLOOD,
573 "Bit change did not take")
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700574 # Set it back
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700575 rv = port_config_set(self.controller, of_port, config,
576 ofp.OFPPC_NO_FLOOD, basic_logger)
577 self.assertTrue(rv != -1, "Error sending port mod")
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700578
Ken Chiangaeb23d62012-08-23 21:20:07 -0700579class PortConfigModErr(SimpleProtocol):
580 """
581 Modify a bit in port config on an invalid port and verify
582 error message is received.
583 """
584
585 def runTest(self):
586 basic_logger.info("Running " + str(self))
587
588 # pick a random bad port number
589 bad_port = random.randint(1, ofp.OFPP_MAX)
590 count = 0
591 while (count < 50) and (bad_port in basic_port_map.keys()):
592 bad_port = random.randint(1, ofp.OFPP_MAX)
593 count = count + 1
594 self.assertTrue(count < 50, "Error selecting bad port")
595 basic_logger.info("Select " + str(bad_port) + " as invalid port")
596
597 rv = port_config_set(self.controller, bad_port,
598 ofp.OFPPC_NO_FLOOD, ofp.OFPPC_NO_FLOOD,
599 basic_logger)
600 self.assertTrue(rv != -1, "Error sending port mod")
601
602 # poll for error message
603 while True:
604 (response, raw) = self.controller.poll(ofp.OFPT_ERROR, 2)
605 if not response: # Timeout
606 break
607 if response.code == ofp.OFPPMFC_BAD_PORT:
608 basic_logger.info("Received error message with OFPPMFC_BAD_PORT code")
609 break
610 if not basic_config["relax"]: # Only one attempt to match
611 break
612 count += 1
613 if count > 10: # Too many tries
614 break
615
616 self.assertTrue(response is not None, 'Did not receive error message')
617
Dan Talaycoe605b1b2012-09-18 06:56:20 -0700618class BadMessage(SimpleProtocol):
619 """
620 Send a message with a bad type and verify an error is returned
621 """
622
623 def runTest(self):
624 basic_logger.info("Running " + str(self))
625 request = illegal_message.illegal_message_type()
626
627 reply, pkt = self.controller.transact(request, timeout=10)
628 self.assertTrue(reply is not None, "Did not get response to bad req")
629 self.assertTrue(reply.header.type == ofp.OFPT_ERROR,
630 "reply not an error message")
631 self.assertTrue(reply.type == ofp.OFPET_BAD_REQUEST,
632 "reply error type is not bad request")
633 self.assertTrue(reply.code == ofp.OFPBRC_BAD_TYPE,
634 "reply error code is not bad type")
635
Dan Talaycodba244e2010-02-15 14:08:53 -0800636if __name__ == "__main__":
Dan Talayco2c0dba32010-03-06 22:47:06 -0800637 print "Please run through oft script: ./oft --test_spec=basic"