blob: 0112734804e36ac56ad61891b813630d81beefd3 [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
Rich Laneda3b5ad2012-10-03 09:05:32 -070034from oftest.testutils import *
Dan Talayco6ce963a2010-03-07 21:58:13 -080035
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_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
Dan Talayco48370102010-03-03 15:17:33 -080054 global basic_config
55
Dan Talayco48370102010-03-03 15:17:33 -080056 basic_port_map = config["port_map"]
57 basic_config = config
Dan Talayco48370102010-03-03 15:17:33 -080058
Dan Talayco6ce963a2010-03-07 21:58:13 -080059class SimpleProtocol(unittest.TestCase):
Dan Talaycodba244e2010-02-15 14:08:53 -080060 """
61 Root class for setting up the controller
62 """
63
Dan Talaycoef701f42010-05-07 09:22:35 -070064 def sig_handler(self, v1, v2):
Rich Lane9a003812012-10-04 17:17:59 -070065 logging.critical("Received interrupt signal; exiting")
Dan Talayco710438c2010-02-18 15:16:07 -080066 print "Received interrupt signal; exiting"
Dan Talayco2c0dba32010-03-06 22:47:06 -080067 self.clean_shutdown = False
68 self.tearDown()
Rich Lane58cf05f2012-07-11 16:41:47 -070069 raise KeyboardInterrupt
Dan Talayco710438c2010-02-18 15:16:07 -080070
Dan Talaycodba244e2010-02-15 14:08:53 -080071 def setUp(self):
Dan Talayco285a8382010-07-20 14:06:55 -070072 self.config = basic_config
Ed Swierk022d02e2012-08-22 06:26:36 -070073 #@todo Test cases shouldn't monkey with signals; move SIGINT handler
74 # to top-level oft
75 try:
76 signal.signal(signal.SIGINT, self.sig_handler)
77 except ValueError, e:
Rich Lane9a003812012-10-04 17:17:59 -070078 logging.info("Could not set SIGINT handler: %s" % e)
79 logging.info("** START TEST CASE " + str(self))
Dan Talayco2c0dba32010-03-06 22:47:06 -080080 self.controller = controller.Controller(
81 host=basic_config["controller_host"],
82 port=basic_config["controller_port"])
Dan Talaycof8f41402010-03-12 22:17:39 -080083 # clean_shutdown should be set to False to force quit app
Dan Talayco2c0dba32010-03-06 22:47:06 -080084 self.clean_shutdown = True
Dan Talayco710438c2010-02-18 15:16:07 -080085 self.controller.start()
Dan Talaycoef701f42010-05-07 09:22:35 -070086 #@todo Add an option to wait for a pkt transaction to ensure version
87 # compatibilty?
Dan Talayco710438c2010-02-18 15:16:07 -080088 self.controller.connect(timeout=20)
Dan Talayco5d0f7cc2012-09-19 11:28:58 -070089
90 # By default, respond to echo requests
91 self.controller.keep_alive = True
92
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)")
Rich Lane9a003812012-10-04 17:17:59 -070097 logging.info("Connected " + str(self.controller.switch_addr))
Ed Swierkc7193a22012-08-22 06:51:02 -070098 request = message.features_request()
Dan Talaycoc689a792012-09-28 14:22:53 -070099 reply, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700100 self.assertTrue(reply is not None,
101 "Did not complete features_request for handshake")
Ed Swierkc7193a22012-08-22 06:51:02 -0700102 self.supported_actions = reply.actions
Rich Lane9a003812012-10-04 17:17:59 -0700103 logging.info("Supported actions: " + hex(self.supported_actions))
Dan Talaycodba244e2010-02-15 14:08:53 -0800104
Dan Talayco677cc112012-03-27 10:28:58 -0700105 def inheritSetup(self, parent):
106 """
107 Inherit the setup of a parent
108
109 This allows running at test from within another test. Do the
110 following:
111
112 sub_test = SomeTestClass() # Create an instance of the test class
113 sub_test.inheritSetup(self) # Inherit setup of parent
114 sub_test.runTest() # Run the test
115
116 Normally, only the parent's setUp and tearDown are called and
117 the state after the sub_test is run must be taken into account
118 by subsequent operations.
119 """
Dan Talayco677cc112012-03-27 10:28:58 -0700120 self.config = parent.config
Rich Lane9a003812012-10-04 17:17:59 -0700121 logging.info("** Setup " + str(self) + " inheriting from "
Dan Talayco677cc112012-03-27 10:28:58 -0700122 + str(parent))
123 self.controller = parent.controller
Ed Swierk6192e512012-08-22 11:41:40 -0700124 self.supported_actions = parent.supported_actions
Dan Talayco677cc112012-03-27 10:28:58 -0700125
Dan Talaycodba244e2010-02-15 14:08:53 -0800126 def tearDown(self):
Rich Lane9a003812012-10-04 17:17:59 -0700127 logging.info("** END TEST CASE " + str(self))
Dan Talaycodba244e2010-02-15 14:08:53 -0800128 self.controller.shutdown()
Dan Talayco2c0dba32010-03-06 22:47:06 -0800129 #@todo Review if join should be done on clean_shutdown
Dan Talaycof8f41402010-03-12 22:17:39 -0800130 if self.clean_shutdown:
131 self.controller.join()
Dan Talaycodba244e2010-02-15 14:08:53 -0800132
133 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -0800134 # Just a simple sanity check as illustration
Rich Lane9a003812012-10-04 17:17:59 -0700135 logging.info("Running simple proto test")
Dan Talayco710438c2010-02-18 15:16:07 -0800136 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800137 str(self) + 'No connection to switch')
138
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700139 def assertTrue(self, cond, msg):
140 if not cond:
Rich Lane9a003812012-10-04 17:17:59 -0700141 logging.error("** FAILED ASSERTION: " + msg)
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700142 unittest.TestCase.assertTrue(self, cond, msg)
143
Dan Talaycoc24aaae2010-07-08 14:05:24 -0700144test_prio["SimpleProtocol"] = 1
145
Dan Talayco6ce963a2010-03-07 21:58:13 -0800146class SimpleDataPlane(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800147 """
148 Root class that sets up the controller and dataplane
149 """
150 def setUp(self):
Dan Talayco6ce963a2010-03-07 21:58:13 -0800151 SimpleProtocol.setUp(self)
Jeffrey Townsend4d5ca922012-07-11 11:37:35 -0700152 self.dataplane = dataplane.DataPlane(self.config)
Dan Talayco48370102010-03-03 15:17:33 -0800153 for of_port, ifname in basic_port_map.items():
Dan Talaycodba244e2010-02-15 14:08:53 -0800154 self.dataplane.port_add(ifname, of_port)
155
Dan Talayco677cc112012-03-27 10:28:58 -0700156 def inheritSetup(self, parent):
157 """
158 Inherit the setup of a parent
159
160 See SimpleProtocol.inheritSetup
161 """
162 SimpleProtocol.inheritSetup(self, parent)
163 self.dataplane = parent.dataplane
164
Dan Talaycodba244e2010-02-15 14:08:53 -0800165 def tearDown(self):
Rich Lane9a003812012-10-04 17:17:59 -0700166 logging.info("Teardown for simple dataplane test")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800167 SimpleProtocol.tearDown(self)
Rich Lane58cf05f2012-07-11 16:41:47 -0700168 if hasattr(self, 'dataplane'):
169 self.dataplane.kill(join_threads=self.clean_shutdown)
Rich Lane9a003812012-10-04 17:17:59 -0700170 logging.info("Teardown done")
Dan Talaycodba244e2010-02-15 14:08:53 -0800171
172 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -0800173 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800174 str(self) + 'No connection to switch')
175 # self.dataplane.show()
176 # Would like an assert that checks the data plane
177
Dan Talayco551befa2010-07-15 17:05:32 -0700178class DataPlaneOnly(unittest.TestCase):
179 """
180 Root class that sets up only the dataplane
181 """
182
183 def sig_handler(self, v1, v2):
Rich Lane9a003812012-10-04 17:17:59 -0700184 logging.critical("Received interrupt signal; exiting")
Dan Talayco551befa2010-07-15 17:05:32 -0700185 print "Received interrupt signal; exiting"
186 self.clean_shutdown = False
187 self.tearDown()
Rich Lane58cf05f2012-07-11 16:41:47 -0700188 raise KeyboardInterrupt
Dan Talayco551befa2010-07-15 17:05:32 -0700189
190 def setUp(self):
Shudong Zhoue3582a52012-08-03 20:46:50 -0700191 self.clean_shutdown = True
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:
Rich Lane9a003812012-10-04 17:17:59 -0700198 logging.info("Could not set SIGINT handler: %s" % e)
199 logging.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):
Rich Lane9a003812012-10-04 17:17:59 -0700205 logging.info("Teardown for simple dataplane test")
Dan Talayco551befa2010-07-15 17:05:32 -0700206 self.dataplane.kill(join_threads=self.clean_shutdown)
Rich Lane9a003812012-10-04 17:17:59 -0700207 logging.info("Teardown done")
Dan Talayco551befa2010-07-15 17:05:32 -0700208
209 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700210 logging.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 Talaycoa6cea8b2012-09-18 06:58:54 -0700214test_prio["DataPlaneOnly"] = -1
215
Dan Talayco6ce963a2010-03-07 21:58:13 -0800216class Echo(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800217 """
218 Test echo response with no data
219 """
220 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800221 request = message.echo_request()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800222 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700223 self.assertTrue(response is not None,
224 "Did not get echo reply")
Dan Talayco2c0dba32010-03-06 22:47:06 -0800225 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800226 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800227 self.assertEqual(request.header.xid, response.header.xid,
228 'response xid != request xid')
229 self.assertEqual(len(response.data), 0, 'response data non-empty')
230
Dan Talayco6ce963a2010-03-07 21:58:13 -0800231class EchoWithData(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800232 """
233 Test echo response with short string data
234 """
235 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800236 request = message.echo_request()
Dan Talaycodba244e2010-02-15 14:08:53 -0800237 request.data = 'OpenFlow Will Rule The World'
Dan Talaycoe226eb12010-02-18 23:06:30 -0800238 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700239 self.assertTrue(response is not None,
240 "Did not get echo reply (with data)")
Dan Talayco2c0dba32010-03-06 22:47:06 -0800241 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800242 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800243 self.assertEqual(request.header.xid, response.header.xid,
244 'response xid != request xid')
245 self.assertEqual(request.data, response.data,
246 'response data does not match request')
247
Dan Talayco6ce963a2010-03-07 21:58:13 -0800248class PacketIn(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800249 """
250 Test packet in function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800251
252 Send a packet to each dataplane port and verify that a packet
253 in message is received from the controller for each
Dan Talaycodba244e2010-02-15 14:08:53 -0800254 """
255 def runTest(self):
256 # Construct packet to send to dataplane
Dan Talaycoe226eb12010-02-18 23:06:30 -0800257 # Send packet to dataplane, once to each port
Dan Talaycodba244e2010-02-15 14:08:53 -0800258 # Poll controller with expect message type packet in
Dan Talaycoe226eb12010-02-18 23:06:30 -0800259
Rich Lane9a003812012-10-04 17:17:59 -0700260 rc = delete_all_flows(self.controller)
Dan Talayco6ce963a2010-03-07 21:58:13 -0800261 self.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talayco0fc08bd2012-04-09 16:56:18 -0700262 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800263
Ken Chiangaeb23d62012-08-23 21:20:07 -0700264 vid = test_param_get(self.config, 'vid', default=TEST_VID_DEFAULT)
265
Dan Talayco48370102010-03-03 15:17:33 -0800266 for of_port in basic_port_map.keys():
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700267 for pkt, pt in [
268 (simple_tcp_packet(), "simple TCP packet"),
Ken Chiangaeb23d62012-08-23 21:20:07 -0700269 (simple_tcp_packet(dl_vlan_enable=True,dl_vlan=vid,pktlen=108),
270 "simple tagged TCP packet"),
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700271 (simple_eth_packet(), "simple Ethernet packet"),
272 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
Dan Talaycodba244e2010-02-15 14:08:53 -0800273
Rich Lane9a003812012-10-04 17:17:59 -0700274 logging.info("PKT IN test with %s, port %s" % (pt, of_port))
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700275 self.dataplane.send(of_port, str(pkt))
276 #@todo Check for unexpected messages?
277 count = 0
278 while True:
Dan Talaycoc689a792012-09-28 14:22:53 -0700279 (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN)
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700280 if not response: # Timeout
281 break
Ed Swierk506614a2012-03-29 08:16:59 -0700282 if dataplane.match_exp_pkt(pkt, response.data): # Got match
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700283 break
284 if not basic_config["relax"]: # Only one attempt to match
285 break
286 count += 1
287 if count > 10: # Too many tries
288 break
Dan Talayco48370102010-03-03 15:17:33 -0800289
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700290 self.assertTrue(response is not None,
291 'Packet in message not received on port ' +
292 str(of_port))
Ed Swierk506614a2012-03-29 08:16:59 -0700293 if not dataplane.match_exp_pkt(pkt, response.data):
Rich Lane9a003812012-10-04 17:17:59 -0700294 logging.debug("Sent %s" % format_packet(pkt))
295 logging.debug("Resp %s" % format_packet(response.data))
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700296 self.assertTrue(False,
297 'Response packet does not match send packet' +
298 ' for port ' + str(of_port))
Dan Talaycodba244e2010-02-15 14:08:53 -0800299
Ed Swierk3ae7f712012-08-22 06:45:25 -0700300class PacketInDefaultDrop(SimpleDataPlane):
301 """
302 Test packet in function
303
304 Send a packet to each dataplane port and verify that a packet
305 in message is received from the controller for each
306 """
307 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700308 rc = delete_all_flows(self.controller)
Ed Swierk3ae7f712012-08-22 06:45:25 -0700309 self.assertEqual(rc, 0, "Failed to delete all flows")
310 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
311
312 for of_port in basic_port_map.keys():
313 pkt = simple_tcp_packet()
314 self.dataplane.send(of_port, str(pkt))
315 count = 0
316 while True:
Dan Talaycoc689a792012-09-28 14:22:53 -0700317 (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN)
Ed Swierk3ae7f712012-08-22 06:45:25 -0700318 if not response: # Timeout
319 break
320 if dataplane.match_exp_pkt(pkt, response.data): # Got match
321 break
322 if not basic_config["relax"]: # Only one attempt to match
323 break
324 count += 1
325 if count > 10: # Too many tries
326 break
327
328 self.assertTrue(response is None,
329 'Packet in message received on port ' +
330 str(of_port))
331
332test_prio["PacketInDefaultDrop"] = -1
333
Jeffrey Townsend4d5ca922012-07-11 11:37:35 -0700334
Dan Talayco1f648cb2012-05-03 09:37:56 -0700335class PacketInBroadcastCheck(SimpleDataPlane):
336 """
337 Check if bcast pkts leak when no flows are present
338
339 Clear the flow table
340 Send in a broadcast pkt
341 Look for the packet on other dataplane ports.
342 """
343 def runTest(self):
344 # Need at least two ports
345 self.assertTrue(len(basic_port_map) > 1, "Too few ports for test")
346
Rich Lane9a003812012-10-04 17:17:59 -0700347 rc = delete_all_flows(self.controller)
Dan Talayco1f648cb2012-05-03 09:37:56 -0700348 self.assertEqual(rc, 0, "Failed to delete all flows")
349 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
350
351 of_ports = basic_port_map.keys()
352 d_port = of_ports[0]
353 pkt = simple_eth_packet(dl_dst='ff:ff:ff:ff:ff:ff')
354
Rich Lane9a003812012-10-04 17:17:59 -0700355 logging.info("BCast Leak Test, send to port %s" % d_port)
Dan Talayco1f648cb2012-05-03 09:37:56 -0700356 self.dataplane.send(d_port, str(pkt))
357
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700358 (of_port, pkt_in, pkt_time) = self.dataplane.poll(exp_pkt=pkt)
Dan Talayco1f648cb2012-05-03 09:37:56 -0700359 self.assertTrue(pkt_in is None,
360 'BCast packet received on port ' + str(of_port))
361
362test_prio["PacketInBroadcastCheck"] = -1
363
Dan Talayco6ce963a2010-03-07 21:58:13 -0800364class PacketOut(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800365 """
366 Test packet out function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800367
368 Send packet out message to controller for each dataplane port and
369 verify the packet appears on the appropriate dataplane port
Dan Talaycodba244e2010-02-15 14:08:53 -0800370 """
371 def runTest(self):
372 # Construct packet to send to dataplane
373 # Send packet to dataplane
374 # Poll controller with expect message type packet in
Dan Talayco41eae8b2010-03-10 13:57:06 -0800375
Rich Lane9a003812012-10-04 17:17:59 -0700376 rc = delete_all_flows(self.controller)
Dan Talayco41eae8b2010-03-10 13:57:06 -0800377 self.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talaycodba244e2010-02-15 14:08:53 -0800378
379 # These will get put into function
Dan Talayco48370102010-03-03 15:17:33 -0800380 of_ports = basic_port_map.keys()
381 of_ports.sort()
382 for dp_port in of_ports:
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700383 for outpkt, opt in [
384 (simple_tcp_packet(), "simple TCP packet"),
385 (simple_eth_packet(), "simple Ethernet packet"),
386 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
Dan Talaycodba244e2010-02-15 14:08:53 -0800387
Rich Lane9a003812012-10-04 17:17:59 -0700388 logging.info("PKT OUT test with %s, port %s" % (opt, dp_port))
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700389 msg = message.packet_out()
390 msg.data = str(outpkt)
391 act = action.action_output()
392 act.port = dp_port
393 self.assertTrue(msg.actions.add(act), 'Could not add action to msg')
Dan Talaycodba244e2010-02-15 14:08:53 -0800394
Rich Lane9a003812012-10-04 17:17:59 -0700395 logging.info("PacketOut to: " + str(dp_port))
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700396 rv = self.controller.message_send(msg)
397 self.assertTrue(rv == 0, "Error sending out message")
Dan Talaycodba244e2010-02-15 14:08:53 -0800398
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700399 exp_pkt_arg = None
400 exp_port = None
401 if basic_config["relax"]:
402 exp_pkt_arg = outpkt
403 exp_port = dp_port
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700404 (of_port, pkt, pkt_time) = self.dataplane.poll(port_number=exp_port,
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700405 exp_pkt=exp_pkt_arg)
406
407 self.assertTrue(pkt is not None, 'Packet not received')
Rich Lane9a003812012-10-04 17:17:59 -0700408 logging.info("PacketOut: got pkt from " + str(of_port))
Ed Swierk0aeff8c2012-03-23 20:27:18 -0700409 if of_port is not None:
410 self.assertEqual(of_port, dp_port, "Unexpected receive port")
Ed Swierk506614a2012-03-29 08:16:59 -0700411 if not dataplane.match_exp_pkt(outpkt, pkt):
Rich Lane9a003812012-10-04 17:17:59 -0700412 logging.debug("Sent %s" % format_packet(outpkt))
413 logging.debug("Resp %s" % format_packet(
Dan Talayco2baf8b52012-03-30 09:55:42 -0700414 str(pkt)[:len(str(outpkt))]))
Dan Talaycodc6fca32012-03-30 10:05:49 -0700415 self.assertEqual(str(outpkt), str(pkt)[:len(str(outpkt))],
416 'Response packet does not match send packet')
Dan Talaycodba244e2010-02-15 14:08:53 -0800417
Ken Chiang1bf01602012-04-04 10:48:23 -0700418class PacketOutMC(SimpleDataPlane):
419 """
420 Test packet out to multiple output ports
421
422 Send packet out message to controller for 1 to N dataplane ports and
423 verify the packet appears on the appropriate ports
424 """
425 def runTest(self):
426 # Construct packet to send to dataplane
427 # Send packet to dataplane
428 # Poll controller with expect message type packet in
429
Rich Lane9a003812012-10-04 17:17:59 -0700430 rc = delete_all_flows(self.controller)
Ken Chiang1bf01602012-04-04 10:48:23 -0700431 self.assertEqual(rc, 0, "Failed to delete all flows")
432
433 # These will get put into function
434 of_ports = basic_port_map.keys()
435 random.shuffle(of_ports)
436 for num_ports in range(1,len(of_ports)+1):
437 for outpkt, opt in [
438 (simple_tcp_packet(), "simple TCP packet"),
439 (simple_eth_packet(), "simple Ethernet packet"),
440 (simple_eth_packet(pktlen=40), "tiny Ethernet packet")]:
441
442 dp_ports = of_ports[0:num_ports]
Rich Lane9a003812012-10-04 17:17:59 -0700443 logging.info("PKT OUT test with " + opt +
Ken Chiang1bf01602012-04-04 10:48:23 -0700444 ", ports " + str(dp_ports))
445 msg = message.packet_out()
446 msg.data = str(outpkt)
447 act = action.action_output()
448 for i in range(0,num_ports):
449 act.port = dp_ports[i]
450 self.assertTrue(msg.actions.add(act),
451 'Could not add action to msg')
452
Rich Lane9a003812012-10-04 17:17:59 -0700453 logging.info("PacketOut to: " + str(dp_ports))
Ken Chiang1bf01602012-04-04 10:48:23 -0700454 rv = self.controller.message_send(msg)
455 self.assertTrue(rv == 0, "Error sending out message")
456
457 receive_pkt_check(self.dataplane, outpkt, dp_ports,
458 set(of_ports).difference(dp_ports),
Rich Lane9a003812012-10-04 17:17:59 -0700459 self, basic_config)
Ken Chiang1bf01602012-04-04 10:48:23 -0700460
Dan Talayco6ce963a2010-03-07 21:58:13 -0800461class FlowStatsGet(SimpleProtocol):
462 """
463 Get stats
Dan Talayco2c0dba32010-03-06 22:47:06 -0800464
Dan Talayco6ce963a2010-03-07 21:58:13 -0800465 Simply verify stats get transaction
466 """
467 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700468 logging.info("Running StatsGet")
469 logging.info("Inserting trial flow")
Dan Talayco677c0b72011-08-23 22:53:38 -0700470 request = flow_mod_gen(basic_port_map, True)
Dan Talayco41eae8b2010-03-10 13:57:06 -0800471 rv = self.controller.message_send(request)
472 self.assertTrue(rv != -1, "Failed to insert test flow")
473
Rich Lane9a003812012-10-04 17:17:59 -0700474 logging.info("Sending flow request")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800475 request = message.flow_stats_request()
476 request.out_port = ofp.OFPP_NONE
Dan Talayco41eae8b2010-03-10 13:57:06 -0800477 request.table_id = 0xff
478 request.match.wildcards = 0 # ofp.OFPFW_ALL
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700479 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700480 self.assertTrue(response is not None,
481 "Did not get response for flow stats")
Rich Lane9a003812012-10-04 17:17:59 -0700482 logging.debug(response.show())
Dan Talayco6ce963a2010-03-07 21:58:13 -0800483
Dan Talayco677c0b72011-08-23 22:53:38 -0700484test_prio["FlowStatsGet"] = -1
485
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700486class TableStatsGet(SimpleProtocol):
487 """
488 Get table stats
489
490 Simply verify table stats get transaction
491 """
492 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700493 logging.info("Running TableStatsGet")
494 logging.info("Inserting trial flow")
Dan Talayco677c0b72011-08-23 22:53:38 -0700495 request = flow_mod_gen(basic_port_map, True)
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700496 rv = self.controller.message_send(request)
497 self.assertTrue(rv != -1, "Failed to insert test flow")
498
Rich Lane9a003812012-10-04 17:17:59 -0700499 logging.info("Sending table stats request")
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700500 request = message.table_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700501 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700502 self.assertTrue(response is not None,
503 "Did not get reply for table stats")
Rich Lane9a003812012-10-04 17:17:59 -0700504 logging.debug(response.show())
Dan Talayco79c6c4d2010-06-08 14:01:53 -0700505
Ed Swierkae74c362012-04-02 08:21:41 -0700506class DescStatsGet(SimpleProtocol):
507 """
508 Get stats
509
510 Simply verify stats get transaction
511 """
512 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700513 logging.info("Running DescStatsGet")
Ed Swierkae74c362012-04-02 08:21:41 -0700514
Rich Lane9a003812012-10-04 17:17:59 -0700515 logging.info("Sending stats request")
Ed Swierkae74c362012-04-02 08:21:41 -0700516 request = message.desc_stats_request()
Rich Lanec8aaa3e2012-07-26 19:28:02 -0700517 response, pkt = self.controller.transact(request)
Dan Talayco97d4f362012-09-18 03:22:09 -0700518 self.assertTrue(response is not None,
519 "Did not get reply for desc stats")
Rich Lane9a003812012-10-04 17:17:59 -0700520 logging.debug(response.show())
Ed Swierkae74c362012-04-02 08:21:41 -0700521
Dan Talayco6ce963a2010-03-07 21:58:13 -0800522class FlowMod(SimpleProtocol):
523 """
524 Insert a flow
525
526 Simple verification of a flow mod transaction
527 """
528
529 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700530 logging.info("Running " + str(self))
Dan Talayco677c0b72011-08-23 22:53:38 -0700531 request = flow_mod_gen(basic_port_map, True)
Dan Talayco6ce963a2010-03-07 21:58:13 -0800532 rv = self.controller.message_send(request)
Dan Talayco41eae8b2010-03-10 13:57:06 -0800533 self.assertTrue(rv != -1, "Error installing flow mod")
534
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700535class PortConfigMod(SimpleProtocol):
536 """
537 Modify a bit in port config and verify changed
538
539 Get the switch configuration, modify the port configuration
540 and write it back; get the config again and verify changed.
541 Then set it back to the way it was.
542 """
543
544 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700545 logging.info("Running " + str(self))
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700546 for of_port, ifname in basic_port_map.items(): # Grab first port
547 break
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700548
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700549 (hw_addr, config, advert) = \
Rich Lane9a003812012-10-04 17:17:59 -0700550 port_config_get(self.controller, of_port)
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700551 self.assertTrue(config is not None, "Did not get port config")
552
Rich Lane9a003812012-10-04 17:17:59 -0700553 logging.debug("No flood bit port " + str(of_port) + " is now " +
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700554 str(config & ofp.OFPPC_NO_FLOOD))
555
556 rv = port_config_set(self.controller, of_port,
Rich Lane9a003812012-10-04 17:17:59 -0700557 config ^ ofp.OFPPC_NO_FLOOD, ofp.OFPPC_NO_FLOOD)
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700558 self.assertTrue(rv != -1, "Error sending port mod")
559
560 # Verify change took place with same feature request
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700561 (hw_addr, config2, advert) = \
Rich Lane9a003812012-10-04 17:17:59 -0700562 port_config_get(self.controller, of_port)
563 logging.debug("No flood bit port " + str(of_port) + " is now " +
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700564 str(config2 & ofp.OFPPC_NO_FLOOD))
565 self.assertTrue(config2 is not None, "Did not get port config2")
566 self.assertTrue(config2 & ofp.OFPPC_NO_FLOOD !=
567 config & ofp.OFPPC_NO_FLOOD,
568 "Bit change did not take")
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700569 # Set it back
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700570 rv = port_config_set(self.controller, of_port, config,
Rich Lane9a003812012-10-04 17:17:59 -0700571 ofp.OFPPC_NO_FLOOD)
Dan Talayco9f47f4d2010-06-03 13:54:37 -0700572 self.assertTrue(rv != -1, "Error sending port mod")
Dan Talaycob3f43fe2010-05-13 14:24:20 -0700573
Ken Chiangaeb23d62012-08-23 21:20:07 -0700574class PortConfigModErr(SimpleProtocol):
575 """
576 Modify a bit in port config on an invalid port and verify
577 error message is received.
578 """
579
580 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700581 logging.info("Running " + str(self))
Ken Chiangaeb23d62012-08-23 21:20:07 -0700582
583 # pick a random bad port number
584 bad_port = random.randint(1, ofp.OFPP_MAX)
585 count = 0
586 while (count < 50) and (bad_port in basic_port_map.keys()):
587 bad_port = random.randint(1, ofp.OFPP_MAX)
588 count = count + 1
589 self.assertTrue(count < 50, "Error selecting bad port")
Rich Lane9a003812012-10-04 17:17:59 -0700590 logging.info("Select " + str(bad_port) + " as invalid port")
Ken Chiangaeb23d62012-08-23 21:20:07 -0700591
592 rv = port_config_set(self.controller, bad_port,
Rich Lane9a003812012-10-04 17:17:59 -0700593 ofp.OFPPC_NO_FLOOD, ofp.OFPPC_NO_FLOOD)
Ken Chiangaeb23d62012-08-23 21:20:07 -0700594 self.assertTrue(rv != -1, "Error sending port mod")
595
596 # poll for error message
597 while True:
Dan Talaycoc689a792012-09-28 14:22:53 -0700598 (response, raw) = self.controller.poll(ofp.OFPT_ERROR)
Ken Chiangaeb23d62012-08-23 21:20:07 -0700599 if not response: # Timeout
600 break
601 if response.code == ofp.OFPPMFC_BAD_PORT:
Rich Lane9a003812012-10-04 17:17:59 -0700602 logging.info("Received error message with OFPPMFC_BAD_PORT code")
Ken Chiangaeb23d62012-08-23 21:20:07 -0700603 break
604 if not basic_config["relax"]: # Only one attempt to match
605 break
606 count += 1
607 if count > 10: # Too many tries
608 break
609
610 self.assertTrue(response is not None, 'Did not receive error message')
611
Dan Talaycoe605b1b2012-09-18 06:56:20 -0700612class BadMessage(SimpleProtocol):
613 """
614 Send a message with a bad type and verify an error is returned
615 """
616
617 def runTest(self):
Rich Lane9a003812012-10-04 17:17:59 -0700618 logging.info("Running " + str(self))
Dan Talaycoe605b1b2012-09-18 06:56:20 -0700619 request = illegal_message.illegal_message_type()
620
Dan Talaycoc689a792012-09-28 14:22:53 -0700621 reply, pkt = self.controller.transact(request)
Dan Talaycoe605b1b2012-09-18 06:56:20 -0700622 self.assertTrue(reply is not None, "Did not get response to bad req")
623 self.assertTrue(reply.header.type == ofp.OFPT_ERROR,
624 "reply not an error message")
625 self.assertTrue(reply.type == ofp.OFPET_BAD_REQUEST,
626 "reply error type is not bad request")
627 self.assertTrue(reply.code == ofp.OFPBRC_BAD_TYPE,
628 "reply error code is not bad type")
629
Dan Talaycodba244e2010-02-15 14:08:53 -0800630if __name__ == "__main__":
Dan Talayco2c0dba32010-03-06 22:47:06 -0800631 print "Please run through oft script: ./oft --test_spec=basic"