blob: feda04f8edad8c318b3ed9a9dac166564cbaed2a [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 Talayco6ce963a2010-03-07 21:58:13 -080032from testutils import *
33
34#@var basic_port_map Local copy of the configuration map from OF port
35# numbers to OS interfaces
Dan Talayco48370102010-03-03 15:17:33 -080036basic_port_map = None
Dan Talayco6ce963a2010-03-07 21:58:13 -080037#@var basic_logger Local logger object
Dan Talayco48370102010-03-03 15:17:33 -080038basic_logger = None
Dan Talayco6ce963a2010-03-07 21:58:13 -080039#@var basic_config Local copy of global configuration data
Dan Talayco48370102010-03-03 15:17:33 -080040basic_config = None
41
Dan Talaycoc24aaae2010-07-08 14:05:24 -070042test_prio = {}
43
Ken Chiangaeb23d62012-08-23 21:20:07 -070044TEST_VID_DEFAULT = 2
45
Dan Talayco48370102010-03-03 15:17:33 -080046def test_set_init(config):
47 """
48 Set up function for basic test classes
49
50 @param config The configuration dictionary; see oft
Dan Talayco48370102010-03-03 15:17:33 -080051 """
52
53 global basic_port_map
54 global basic_logger
55 global basic_config
56
57 basic_logger = logging.getLogger("basic")
58 basic_logger.info("Initializing test set")
59 basic_port_map = config["port_map"]
60 basic_config = config
Dan Talayco48370102010-03-03 15:17:33 -080061
Dan Talayco6ce963a2010-03-07 21:58:13 -080062class SimpleProtocol(unittest.TestCase):
Dan Talaycodba244e2010-02-15 14:08:53 -080063 """
64 Root class for setting up the controller
65 """
66
Dan Talaycoef701f42010-05-07 09:22:35 -070067 def sig_handler(self, v1, v2):
Dan Talayco48370102010-03-03 15:17:33 -080068 basic_logger.critical("Received interrupt signal; exiting")
Dan Talayco710438c2010-02-18 15:16:07 -080069 print "Received interrupt signal; exiting"
Dan Talayco2c0dba32010-03-06 22:47:06 -080070 self.clean_shutdown = False
71 self.tearDown()
Rich Lane58cf05f2012-07-11 16:41:47 -070072 raise KeyboardInterrupt
Dan Talayco710438c2010-02-18 15:16:07 -080073
Dan Talaycodba244e2010-02-15 14:08:53 -080074 def setUp(self):
Dan Talayco551befa2010-07-15 17:05:32 -070075 self.logger = basic_logger
Dan Talayco285a8382010-07-20 14:06:55 -070076 self.config = basic_config
Ed Swierk022d02e2012-08-22 06:26:36 -070077 #@todo Test cases shouldn't monkey with signals; move SIGINT handler
78 # to top-level oft
79 try:
80 signal.signal(signal.SIGINT, self.sig_handler)
81 except ValueError, e:
82 basic_logger.info("Could not set SIGINT handler: %s" % e)
Dan Talayco9f47f4d2010-06-03 13:54:37 -070083 basic_logger.info("** START TEST CASE " + str(self))
Dan Talayco2c0dba32010-03-06 22:47:06 -080084 self.controller = controller.Controller(
85 host=basic_config["controller_host"],
86 port=basic_config["controller_port"])
Dan Talaycof8f41402010-03-12 22:17:39 -080087 # clean_shutdown should be set to False to force quit app
Dan Talayco2c0dba32010-03-06 22:47:06 -080088 self.clean_shutdown = True
Dan Talayco710438c2010-02-18 15:16:07 -080089 self.controller.start()
Dan Talaycoef701f42010-05-07 09:22:35 -070090 #@todo Add an option to wait for a pkt transaction to ensure version
91 # compatibilty?
Dan Talayco710438c2010-02-18 15:16:07 -080092 self.controller.connect(timeout=20)
Dan Talaycoef701f42010-05-07 09:22:35 -070093 if not self.controller.active:
Rich Lane58cf05f2012-07-11 16:41:47 -070094 raise Exception("Controller startup failed")
Dan Talayco677c0b72011-08-23 22:53:38 -070095 if self.controller.switch_addr is None:
Rich Lane58cf05f2012-07-11 16:41:47 -070096 raise Exception("Controller startup failed (no switch addr)")
Dan Talayco48370102010-03-03 15:17:33 -080097 basic_logger.info("Connected " + str(self.controller.switch_addr))
Ed Swierkc7193a22012-08-22 06:51:02 -070098 request = message.features_request()
99 reply, pkt = self.controller.transact(request, timeout=10)
100 self.supported_actions = reply.actions
101 basic_logger.info("Supported actions: " + hex(self.supported_actions))
Dan Talaycodba244e2010-02-15 14:08:53 -0800102
Dan Talayco677cc112012-03-27 10:28:58 -0700103 def inheritSetup(self, parent):
104 """
105 Inherit the setup of a parent
106
107 This allows running at test from within another test. Do the
108 following:
109
110 sub_test = SomeTestClass() # Create an instance of the test class
111 sub_test.inheritSetup(self) # Inherit setup of parent
112 sub_test.runTest() # Run the test
113
114 Normally, only the parent's setUp and tearDown are called and
115 the state after the sub_test is run must be taken into account
116 by subsequent operations.
117 """
118 self.logger = parent.logger
119 self.config = parent.config
120 basic_logger.info("** Setup " + str(self) + " inheriting from "
121 + str(parent))
122 self.controller = parent.controller
Ed Swierk6192e512012-08-22 11:41:40 -0700123 self.supported_actions = parent.supported_actions
Dan Talayco677cc112012-03-27 10:28:58 -0700124
Dan Talaycodba244e2010-02-15 14:08:53 -0800125 def tearDown(self):
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700126 basic_logger.info("** END TEST CASE " + str(self))
Dan Talaycodba244e2010-02-15 14:08:53 -0800127 self.controller.shutdown()
Dan Talayco2c0dba32010-03-06 22:47:06 -0800128 #@todo Review if join should be done on clean_shutdown
Dan Talaycof8f41402010-03-12 22:17:39 -0800129 if self.clean_shutdown:
130 self.controller.join()
Dan Talaycodba244e2010-02-15 14:08:53 -0800131
132 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -0800133 # Just a simple sanity check as illustration
Dan Talayco48370102010-03-03 15:17:33 -0800134 basic_logger.info("Running simple proto test")
Dan Talayco710438c2010-02-18 15:16:07 -0800135 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800136 str(self) + 'No connection to switch')
137
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700138 def assertTrue(self, cond, msg):
139 if not cond:
140 basic_logger.error("** FAILED ASSERTION: " + msg)
141 unittest.TestCase.assertTrue(self, cond, msg)
142
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700143test_prio["SimpleProtocol"] = 1
144
Dan Talayco6ce963a2010-03-07 21:58:13 -0800145class SimpleDataPlane(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800146 """
147 Root class that sets up the controller and dataplane
148 """
149 def setUp(self):
Dan Talayco6ce963a2010-03-07 21:58:13 -0800150 SimpleProtocol.setUp(self)
Jeffrey Townsend4d5ca922012-07-11 11:37:35 -0700151 self.dataplane = dataplane.DataPlane(self.config)
Dan Talayco48370102010-03-03 15:17:33 -0800152 for of_port, ifname in basic_port_map.items():
Dan Talaycodba244e2010-02-15 14:08:53 -0800153 self.dataplane.port_add(ifname, of_port)
154
Dan Talayco677cc112012-03-27 10:28:58 -0700155 def inheritSetup(self, parent):
156 """
157 Inherit the setup of a parent
158
159 See SimpleProtocol.inheritSetup
160 """
161 SimpleProtocol.inheritSetup(self, parent)
162 self.dataplane = parent.dataplane
163
Dan Talaycodba244e2010-02-15 14:08:53 -0800164 def tearDown(self):
Dan Talayco48370102010-03-03 15:17:33 -0800165 basic_logger.info("Teardown for simple dataplane test")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800166 SimpleProtocol.tearDown(self)
Rich Lane58cf05f2012-07-11 16:41:47 -0700167 if hasattr(self, 'dataplane'):
168 self.dataplane.kill(join_threads=self.clean_shutdown)
Dan Talayco48370102010-03-03 15:17:33 -0800169 basic_logger.info("Teardown done")
Dan Talaycodba244e2010-02-15 14:08:53 -0800170
171 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -0800172 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800173 str(self) + 'No connection to switch')
174 # self.dataplane.show()
175 # Would like an assert that checks the data plane
176
Dan Talayco551befa2010-07-15 17:05:32 -0700177class DataPlaneOnly(unittest.TestCase):
178 """
179 Root class that sets up only the dataplane
180 """
181
182 def sig_handler(self, v1, v2):
183 basic_logger.critical("Received interrupt signal; exiting")
184 print "Received interrupt signal; exiting"
185 self.clean_shutdown = False
186 self.tearDown()
Rich Lane58cf05f2012-07-11 16:41:47 -0700187 raise KeyboardInterrupt
Dan Talayco551befa2010-07-15 17:05:32 -0700188
189 def setUp(self):
Shudong Zhoue3582a52012-08-03 20:46:50 -0700190 self.clean_shutdown = True
Dan Talayco551befa2010-07-15 17:05:32 -0700191 self.logger = basic_logger
Dan Talayco285a8382010-07-20 14:06:55 -0700192 self.config = basic_config
Ed Swierk022d02e2012-08-22 06:26:36 -0700193 #@todo Test cases shouldn't monkey with signals; move SIGINT handler
194 # to top-level oft
195 try:
196 signal.signal(signal.SIGINT, self.sig_handler)
197 except ValueError, e:
198 basic_logger.info("Could not set SIGINT handler: %s" % e)
Dan Talayco551befa2010-07-15 17:05:32 -0700199 basic_logger.info("** START DataPlaneOnly CASE " + str(self))
Jeffrey Townsend4d5ca922012-07-11 11:37:35 -0700200 self.dataplane = dataplane.DataPlane(self.config)
Dan Talayco551befa2010-07-15 17:05:32 -0700201 for of_port, ifname in basic_port_map.items():
202 self.dataplane.port_add(ifname, of_port)
203
204 def tearDown(self):
205 basic_logger.info("Teardown for simple dataplane test")
206 self.dataplane.kill(join_threads=self.clean_shutdown)
207 basic_logger.info("Teardown done")
208
209 def runTest(self):
Dan Talaycoba4fd4f2010-07-21 21:49:41 -0700210 basic_logger.info("DataPlaneOnly")
Dan Talayco285a8382010-07-20 14:06:55 -0700211 # self.dataplane.show()
Dan Talayco551befa2010-07-15 17:05:32 -0700212 # Would like an assert that checks the data plane
213
Dan Talayco6ce963a2010-03-07 21:58:13 -0800214class Echo(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800215 """
216 Test echo response with no data
217 """
218 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800219 request = message.echo_request()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800220 response, pkt = self.controller.transact(request)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800221 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800222 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800223 self.assertEqual(request.header.xid, response.header.xid,
224 'response xid != request xid')
225 self.assertEqual(len(response.data), 0, 'response data non-empty')
226
Dan Talayco6ce963a2010-03-07 21:58:13 -0800227class EchoWithData(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800228 """
229 Test echo response with short string data
230 """
231 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800232 request = message.echo_request()
Dan Talaycodba244e2010-02-15 14:08:53 -0800233 request.data = 'OpenFlow Will Rule The World'
Dan Talaycoe226eb12010-02-18 23:06:30 -0800234 response, pkt = self.controller.transact(request)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800235 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800236 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800237 self.assertEqual(request.header.xid, response.header.xid,
238 'response xid != request xid')
239 self.assertEqual(request.data, response.data,
240 'response data does not match request')
241
Dan Talayco6ce963a2010-03-07 21:58:13 -0800242class PacketIn(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800243 """
244 Test packet in function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800245
246 Send a packet to each dataplane port and verify that a packet
247 in message is received from the controller for each
Dan Talaycodba244e2010-02-15 14:08:53 -0800248 """
249 def runTest(self):
250 # Construct packet to send to dataplane
Dan Talaycoe226eb12010-02-18 23:06:30 -0800251 # Send packet to dataplane, once to each port
Dan Talaycodba244e2010-02-15 14:08:53 -0800252 # Poll controller with expect message type packet in
Dan Talaycoe226eb12010-02-18 23:06:30 -0800253
Dan Talayco6ce963a2010-03-07 21:58:13 -0800254 rc = delete_all_flows(self.controller, basic_logger)
255 self.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700256 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800257
Ken Chiangaeb23d62012-08-23 21:20:07 -0700258 vid = test_param_get(self.config, 'vid', default=TEST_VID_DEFAULT)
259
Dan Talayco48370102010-03-03 15:17:33 -0800260 for of_port in basic_port_map.keys():
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700261 for pkt, pt in [
262 (simple_tcp_packet(), "simple TCP packet"),
Ken Chiangaeb23d62012-08-23 21:20:07 -0700263 (simple_tcp_packet(dl_vlan_enable=True,dl_vlan=vid,pktlen=108),
264 "simple tagged TCP packet"),
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700265 (simple_eth_packet(), "simple Ethernet packet"),
266 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
Dan Talaycodba244e2010-02-15 14:08:53 -0800267
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700268 basic_logger.info("PKT IN test with %s, port %s" % (pt, of_port))
269 self.dataplane.send(of_port, str(pkt))
270 #@todo Check for unexpected messages?
271 count = 0
272 while True:
273 (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
274 if not response: # Timeout
275 break
Ed Swierk506614a2012-03-29 08:16:59 -0700276 if dataplane.match_exp_pkt(pkt, response.data): # Got match
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700277 break
278 if not basic_config["relax"]: # Only one attempt to match
279 break
280 count += 1
281 if count > 10: # Too many tries
282 break
Dan Talayco48370102010-03-03 15:17:33 -0800283
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700284 self.assertTrue(response is not None,
285 'Packet in message not received on port ' +
286 str(of_port))
Ed Swierk506614a2012-03-29 08:16:59 -0700287 if not dataplane.match_exp_pkt(pkt, response.data):
Dan Talayco2baf8b52012-03-30 09:55:42 -0700288 basic_logger.debug("Sent %s" % format_packet(pkt))
289 basic_logger.debug("Resp %s" % format_packet(response.data))
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700290 self.assertTrue(False,
291 'Response packet does not match send packet' +
292 ' for port ' + str(of_port))
Dan Talaycodba244e2010-02-15 14:08:53 -0800293
Ed Swierk3ae7f712012-08-22 06:45:25 -0700294class PacketInDefaultDrop(SimpleDataPlane):
295 """
296 Test packet in function
297
298 Send a packet to each dataplane port and verify that a packet
299 in message is received from the controller for each
300 """
301 def runTest(self):
302 rc = delete_all_flows(self.controller, basic_logger)
303 self.assertEqual(rc, 0, "Failed to delete all flows")
304 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
305
306 for of_port in basic_port_map.keys():
307 pkt = simple_tcp_packet()
308 self.dataplane.send(of_port, str(pkt))
309 count = 0
310 while True:
311 (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
312 if not response: # Timeout
313 break
314 if dataplane.match_exp_pkt(pkt, response.data): # Got match
315 break
316 if not basic_config["relax"]: # Only one attempt to match
317 break
318 count += 1
319 if count > 10: # Too many tries
320 break
321
322 self.assertTrue(response is None,
323 'Packet in message received on port ' +
324 str(of_port))
325
326test_prio["PacketInDefaultDrop"] = -1
327
Jeffrey Townsend4d5ca922012-07-11 11:37:35 -0700328
Dan Talayco1f648cb2012-05-03 09:37:56 -0700329class PacketInBroadcastCheck(SimpleDataPlane):
330 """
331 Check if bcast pkts leak when no flows are present
332
333 Clear the flow table
334 Send in a broadcast pkt
335 Look for the packet on other dataplane ports.
336 """
337 def runTest(self):
338 # Need at least two ports
339 self.assertTrue(len(basic_port_map) > 1, "Too few ports for test")
340
341 rc = delete_all_flows(self.controller, basic_logger)
342 self.assertEqual(rc, 0, "Failed to delete all flows")
343 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
344
345 of_ports = basic_port_map.keys()
346 d_port = of_ports[0]
347 pkt = simple_eth_packet(dl_dst='ff:ff:ff:ff:ff:ff')
348
349 basic_logger.info("BCast Leak Test, send to port %s" % d_port)
350 self.dataplane.send(d_port, str(pkt))
351
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700352 (of_port, pkt_in, pkt_time) = self.dataplane.poll(exp_pkt=pkt)
Dan Talayco1f648cb2012-05-03 09:37:56 -0700353 self.assertTrue(pkt_in is None,
354 'BCast packet received on port ' + str(of_port))
355
356test_prio["PacketInBroadcastCheck"] = -1
357
Dan Talayco6ce963a2010-03-07 21:58:13 -0800358class PacketOut(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800359 """
360 Test packet out function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800361
362 Send packet out message to controller for each dataplane port and
363 verify the packet appears on the appropriate dataplane port
Dan Talaycodba244e2010-02-15 14:08:53 -0800364 """
365 def runTest(self):
366 # Construct packet to send to dataplane
367 # Send packet to dataplane
368 # Poll controller with expect message type packet in
Dan Talayco41eae8b2010-03-10 13:57:06 -0800369
370 rc = delete_all_flows(self.controller, basic_logger)
371 self.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talaycodba244e2010-02-15 14:08:53 -0800372
373 # These will get put into function
Dan Talayco48370102010-03-03 15:17:33 -0800374 of_ports = basic_port_map.keys()
375 of_ports.sort()
376 for dp_port in of_ports:
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700377 for outpkt, opt in [
378 (simple_tcp_packet(), "simple TCP packet"),
379 (simple_eth_packet(), "simple Ethernet packet"),
380 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
Dan Talaycodba244e2010-02-15 14:08:53 -0800381
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700382 basic_logger.info("PKT OUT test with %s, port %s" % (opt, dp_port))
383 msg = message.packet_out()
384 msg.data = str(outpkt)
385 act = action.action_output()
386 act.port = dp_port
387 self.assertTrue(msg.actions.add(act), 'Could not add action to msg')
Dan Talaycodba244e2010-02-15 14:08:53 -0800388
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700389 basic_logger.info("PacketOut to: " + str(dp_port))
390 rv = self.controller.message_send(msg)
391 self.assertTrue(rv == 0, "Error sending out message")
Dan Talaycodba244e2010-02-15 14:08:53 -0800392
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700393 exp_pkt_arg = None
394 exp_port = None
395 if basic_config["relax"]:
396 exp_pkt_arg = outpkt
397 exp_port = dp_port
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700398 (of_port, pkt, pkt_time) = self.dataplane.poll(port_number=exp_port,
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700399 exp_pkt=exp_pkt_arg)
400
401 self.assertTrue(pkt is not None, 'Packet not received')
402 basic_logger.info("PacketOut: got pkt from " + str(of_port))
403 if of_port is not None:
404 self.assertEqual(of_port, dp_port, "Unexpected receive port")
Ed Swierk506614a2012-03-29 08:16:59 -0700405 if not dataplane.match_exp_pkt(outpkt, pkt):
Dan Talayco2baf8b52012-03-30 09:55:42 -0700406 basic_logger.debug("Sent %s" % format_packet(outpkt))
407 basic_logger.debug("Resp %s" % format_packet(
408 str(pkt)[:len(str(outpkt))]))
Dan Talaycodc6fca32012-03-30 10:05:49 -0700409 self.assertEqual(str(outpkt), str(pkt)[:len(str(outpkt))],
410 'Response packet does not match send packet')
Dan Talaycodba244e2010-02-15 14:08:53 -0800411
Ken Chiang1bf01602012-04-04 10:48:23 -0700412class PacketOutMC(SimpleDataPlane):
413 """
414 Test packet out to multiple output ports
415
416 Send packet out message to controller for 1 to N dataplane ports and
417 verify the packet appears on the appropriate ports
418 """
419 def runTest(self):
420 # Construct packet to send to dataplane
421 # Send packet to dataplane
422 # Poll controller with expect message type packet in
423
424 rc = delete_all_flows(self.controller, basic_logger)
425 self.assertEqual(rc, 0, "Failed to delete all flows")
426
427 # These will get put into function
428 of_ports = basic_port_map.keys()
429 random.shuffle(of_ports)
430 for num_ports in range(1,len(of_ports)+1):
431 for outpkt, opt in [
432 (simple_tcp_packet(), "simple TCP packet"),
433 (simple_eth_packet(), "simple Ethernet packet"),
434 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
435
436 dp_ports = of_ports[0:num_ports]
437 basic_logger.info("PKT OUT test with " + opt +
438 ", ports " + str(dp_ports))
439 msg = message.packet_out()
440 msg.data = str(outpkt)
441 act = action.action_output()
442 for i in range(0,num_ports):
443 act.port = dp_ports[i]
444 self.assertTrue(msg.actions.add(act),
445 'Could not add action to msg')
446
447 basic_logger.info("PacketOut to: " + str(dp_ports))
448 rv = self.controller.message_send(msg)
449 self.assertTrue(rv == 0, "Error sending out message")
450
451 receive_pkt_check(self.dataplane, outpkt, dp_ports,
452 set(of_ports).difference(dp_ports),
453 self, basic_logger, basic_config)
454
Dan Talayco6ce963a2010-03-07 21:58:13 -0800455class FlowStatsGet(SimpleProtocol):
456 """
457 Get stats
Dan Talayco2c0dba32010-03-06 22:47:06 -0800458
Dan Talayco6ce963a2010-03-07 21:58:13 -0800459 Simply verify stats get transaction
460 """
461 def runTest(self):
462 basic_logger.info("Running StatsGet")
Dan Talayco41eae8b2010-03-10 13:57:06 -0800463 basic_logger.info("Inserting trial flow")
Dan Talayco677c0b72011-08-23 22:53:38 -0700464 request = flow_mod_gen(basic_port_map, True)
Dan Talayco41eae8b2010-03-10 13:57:06 -0800465 rv = self.controller.message_send(request)
466 self.assertTrue(rv != -1, "Failed to insert test flow")
467
468 basic_logger.info("Sending flow request")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800469 request = message.flow_stats_request()
470 request.out_port = ofp.OFPP_NONE
Dan Talayco41eae8b2010-03-10 13:57:06 -0800471 request.table_id = 0xff
472 request.match.wildcards = 0 # ofp.OFPFW_ALL
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700473 response, pkt = self.controller.transact(request)
Dan Talayco6ce963a2010-03-07 21:58:13 -0800474 self.assertTrue(response is not None, "Did not get response")
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700475 basic_logger.debug(response.show())
Dan Talayco6ce963a2010-03-07 21:58:13 -0800476
Dan Talayco677c0b72011-08-23 22:53:38 -0700477test_prio["FlowStatsGet"] = -1
478
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700479class TableStatsGet(SimpleProtocol):
480 """
481 Get table stats
482
483 Simply verify table stats get transaction
484 """
485 def runTest(self):
486 basic_logger.info("Running TableStatsGet")
487 basic_logger.info("Inserting trial flow")
Dan Talayco677c0b72011-08-23 22:53:38 -0700488 request = flow_mod_gen(basic_port_map, True)
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700489 rv = self.controller.message_send(request)
490 self.assertTrue(rv != -1, "Failed to insert test flow")
491
492 basic_logger.info("Sending table stats request")
493 request = message.table_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700494 response, pkt = self.controller.transact(request)
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700495 self.assertTrue(response is not None, "Did not get response")
496 basic_logger.debug(response.show())
497
Ed Swierkae74c362012-04-02 08:21:41 -0700498class DescStatsGet(SimpleProtocol):
499 """
500 Get stats
501
502 Simply verify stats get transaction
503 """
504 def runTest(self):
505 basic_logger.info("Running DescStatsGet")
506
507 basic_logger.info("Sending stats request")
508 request = message.desc_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700509 response, pkt = self.controller.transact(request)
Ed Swierkae74c362012-04-02 08:21:41 -0700510 self.assertTrue(response is not None, "Did not get response")
511 basic_logger.debug(response.show())
512
Dan Talayco6ce963a2010-03-07 21:58:13 -0800513class FlowMod(SimpleProtocol):
514 """
515 Insert a flow
516
517 Simple verification of a flow mod transaction
518 """
519
520 def runTest(self):
521 basic_logger.info("Running " + str(self))
Dan Talayco677c0b72011-08-23 22:53:38 -0700522 request = flow_mod_gen(basic_port_map, True)
Dan Talayco6ce963a2010-03-07 21:58:13 -0800523 rv = self.controller.message_send(request)
Dan Talayco41eae8b2010-03-10 13:57:06 -0800524 self.assertTrue(rv != -1, "Error installing flow mod")
525
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700526class PortConfigMod(SimpleProtocol):
527 """
528 Modify a bit in port config and verify changed
529
530 Get the switch configuration, modify the port configuration
531 and write it back; get the config again and verify changed.
532 Then set it back to the way it was.
533 """
534
535 def runTest(self):
536 basic_logger.info("Running " + str(self))
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700537 for of_port, ifname in basic_port_map.items(): # Grab first port
538 break
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700539
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700540 (hw_addr, config, advert) = \
541 port_config_get(self.controller, of_port, basic_logger)
542 self.assertTrue(config is not None, "Did not get port config")
543
544 basic_logger.debug("No flood bit port " + str(of_port) + " is now " +
545 str(config & ofp.OFPPC_NO_FLOOD))
546
547 rv = port_config_set(self.controller, of_port,
548 config ^ ofp.OFPPC_NO_FLOOD, ofp.OFPPC_NO_FLOOD,
549 basic_logger)
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700550 self.assertTrue(rv != -1, "Error sending port mod")
551
552 # Verify change took place with same feature request
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700553 (hw_addr, config2, advert) = \
554 port_config_get(self.controller, of_port, basic_logger)
555 basic_logger.debug("No flood bit port " + str(of_port) + " is now " +
556 str(config2 & ofp.OFPPC_NO_FLOOD))
557 self.assertTrue(config2 is not None, "Did not get port config2")
558 self.assertTrue(config2 & ofp.OFPPC_NO_FLOOD !=
559 config & ofp.OFPPC_NO_FLOOD,
560 "Bit change did not take")
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700561 # Set it back
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700562 rv = port_config_set(self.controller, of_port, config,
563 ofp.OFPPC_NO_FLOOD, basic_logger)
564 self.assertTrue(rv != -1, "Error sending port mod")
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700565
Ken Chiangaeb23d62012-08-23 21:20:07 -0700566class PortConfigModErr(SimpleProtocol):
567 """
568 Modify a bit in port config on an invalid port and verify
569 error message is received.
570 """
571
572 def runTest(self):
573 basic_logger.info("Running " + str(self))
574
575 # pick a random bad port number
576 bad_port = random.randint(1, ofp.OFPP_MAX)
577 count = 0
578 while (count < 50) and (bad_port in basic_port_map.keys()):
579 bad_port = random.randint(1, ofp.OFPP_MAX)
580 count = count + 1
581 self.assertTrue(count < 50, "Error selecting bad port")
582 basic_logger.info("Select " + str(bad_port) + " as invalid port")
583
584 rv = port_config_set(self.controller, bad_port,
585 ofp.OFPPC_NO_FLOOD, ofp.OFPPC_NO_FLOOD,
586 basic_logger)
587 self.assertTrue(rv != -1, "Error sending port mod")
588
589 # poll for error message
590 while True:
591 (response, raw) = self.controller.poll(ofp.OFPT_ERROR, 2)
592 if not response: # Timeout
593 break
594 if response.code == ofp.OFPPMFC_BAD_PORT:
595 basic_logger.info("Received error message with OFPPMFC_BAD_PORT code")
596 break
597 if not basic_config["relax"]: # Only one attempt to match
598 break
599 count += 1
600 if count > 10: # Too many tries
601 break
602
603 self.assertTrue(response is not None, 'Did not receive error message')
604
Dan Talaycodba244e2010-02-15 14:08:53 -0800605if __name__ == "__main__":
Dan Talayco2c0dba32010-03-06 22:47:06 -0800606 print "Please run through oft script: ./oft --test_spec=basic"