blob: 2f21f5a927a04d0fd4c872912ea507b1cb17c771 [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
24
25import oftest.controller as controller
26import oftest.cstruct as ofp
27import oftest.message as message
28import oftest.dataplane as dataplane
29import oftest.action as action
30
Dan Talayco6ce963a2010-03-07 21:58:13 -080031from testutils import *
32
33#@var basic_port_map Local copy of the configuration map from OF port
34# numbers to OS interfaces
Dan Talayco48370102010-03-03 15:17:33 -080035basic_port_map = None
Dan Talayco6ce963a2010-03-07 21:58:13 -080036#@var basic_logger Local logger object
Dan Talayco48370102010-03-03 15:17:33 -080037basic_logger = None
Dan Talayco6ce963a2010-03-07 21:58:13 -080038#@var basic_config Local copy of global configuration data
Dan Talayco48370102010-03-03 15:17:33 -080039basic_config = None
40
41def test_set_init(config):
42 """
43 Set up function for basic test classes
44
45 @param config The configuration dictionary; see oft
Dan Talayco48370102010-03-03 15:17:33 -080046 """
47
48 global basic_port_map
49 global basic_logger
50 global basic_config
51
52 basic_logger = logging.getLogger("basic")
53 basic_logger.info("Initializing test set")
54 basic_port_map = config["port_map"]
55 basic_config = config
Dan Talayco48370102010-03-03 15:17:33 -080056
Dan Talayco6ce963a2010-03-07 21:58:13 -080057class SimpleProtocol(unittest.TestCase):
Dan Talaycodba244e2010-02-15 14:08:53 -080058 """
59 Root class for setting up the controller
60 """
61
Dan Talaycoef701f42010-05-07 09:22:35 -070062 def sig_handler(self, v1, v2):
Dan Talayco48370102010-03-03 15:17:33 -080063 basic_logger.critical("Received interrupt signal; exiting")
Dan Talayco710438c2010-02-18 15:16:07 -080064 print "Received interrupt signal; exiting"
Dan Talayco2c0dba32010-03-06 22:47:06 -080065 self.clean_shutdown = False
66 self.tearDown()
Dan Talayco710438c2010-02-18 15:16:07 -080067 sys.exit(1)
68
Dan Talaycodba244e2010-02-15 14:08:53 -080069 def setUp(self):
Dan Talayco710438c2010-02-18 15:16:07 -080070 signal.signal(signal.SIGINT, self.sig_handler)
Dan Talayco48370102010-03-03 15:17:33 -080071 basic_logger.info("Setup for " + str(self))
Dan Talayco2c0dba32010-03-06 22:47:06 -080072 self.controller = controller.Controller(
73 host=basic_config["controller_host"],
74 port=basic_config["controller_port"])
Dan Talaycof8f41402010-03-12 22:17:39 -080075 # clean_shutdown should be set to False to force quit app
Dan Talayco2c0dba32010-03-06 22:47:06 -080076 self.clean_shutdown = True
Dan Talayco710438c2010-02-18 15:16:07 -080077 self.controller.start()
Dan Talaycoef701f42010-05-07 09:22:35 -070078 #@todo Add an option to wait for a pkt transaction to ensure version
79 # compatibilty?
Dan Talayco710438c2010-02-18 15:16:07 -080080 self.controller.connect(timeout=20)
Dan Talaycoef701f42010-05-07 09:22:35 -070081 if not self.controller.active:
82 print "Controller startup failed; exiting"
83 sys.exit(1)
Dan Talayco48370102010-03-03 15:17:33 -080084 basic_logger.info("Connected " + str(self.controller.switch_addr))
Dan Talaycodba244e2010-02-15 14:08:53 -080085
86 def tearDown(self):
Dan Talayco48370102010-03-03 15:17:33 -080087 basic_logger.info("Teardown for simple proto test")
Dan Talaycodba244e2010-02-15 14:08:53 -080088 self.controller.shutdown()
Dan Talayco2c0dba32010-03-06 22:47:06 -080089 #@todo Review if join should be done on clean_shutdown
Dan Talaycof8f41402010-03-12 22:17:39 -080090 if self.clean_shutdown:
91 self.controller.join()
Dan Talaycodba244e2010-02-15 14:08:53 -080092
93 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -080094 # Just a simple sanity check as illustration
Dan Talayco48370102010-03-03 15:17:33 -080095 basic_logger.info("Running simple proto test")
Dan Talayco710438c2010-02-18 15:16:07 -080096 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -080097 str(self) + 'No connection to switch')
98
Dan Talayco6ce963a2010-03-07 21:58:13 -080099class SimpleDataPlane(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800100 """
101 Root class that sets up the controller and dataplane
102 """
103 def setUp(self):
Dan Talayco6ce963a2010-03-07 21:58:13 -0800104 SimpleProtocol.setUp(self)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800105 self.dataplane = dataplane.DataPlane()
Dan Talayco48370102010-03-03 15:17:33 -0800106 for of_port, ifname in basic_port_map.items():
Dan Talaycodba244e2010-02-15 14:08:53 -0800107 self.dataplane.port_add(ifname, of_port)
108
109 def tearDown(self):
Dan Talayco48370102010-03-03 15:17:33 -0800110 basic_logger.info("Teardown for simple dataplane test")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800111 SimpleProtocol.tearDown(self)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800112 self.dataplane.kill(join_threads=self.clean_shutdown)
Dan Talayco48370102010-03-03 15:17:33 -0800113 basic_logger.info("Teardown done")
Dan Talaycodba244e2010-02-15 14:08:53 -0800114
115 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -0800116 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800117 str(self) + 'No connection to switch')
118 # self.dataplane.show()
119 # Would like an assert that checks the data plane
120
Dan Talayco6ce963a2010-03-07 21:58:13 -0800121class Echo(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800122 """
123 Test echo response with no data
124 """
125 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800126 request = message.echo_request()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800127 response, pkt = self.controller.transact(request)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800128 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800129 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800130 self.assertEqual(request.header.xid, response.header.xid,
131 'response xid != request xid')
132 self.assertEqual(len(response.data), 0, 'response data non-empty')
133
Dan Talayco6ce963a2010-03-07 21:58:13 -0800134class EchoWithData(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800135 """
136 Test echo response with short string data
137 """
138 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800139 request = message.echo_request()
Dan Talaycodba244e2010-02-15 14:08:53 -0800140 request.data = 'OpenFlow Will Rule The World'
Dan Talaycoe226eb12010-02-18 23:06:30 -0800141 response, pkt = self.controller.transact(request)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800142 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800143 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800144 self.assertEqual(request.header.xid, response.header.xid,
145 'response xid != request xid')
146 self.assertEqual(request.data, response.data,
147 'response data does not match request')
148
Dan Talayco6ce963a2010-03-07 21:58:13 -0800149class PacketIn(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800150 """
151 Test packet in function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800152
153 Send a packet to each dataplane port and verify that a packet
154 in message is received from the controller for each
Dan Talaycodba244e2010-02-15 14:08:53 -0800155 """
156 def runTest(self):
157 # Construct packet to send to dataplane
Dan Talaycoe226eb12010-02-18 23:06:30 -0800158 # Send packet to dataplane, once to each port
Dan Talaycodba244e2010-02-15 14:08:53 -0800159 # Poll controller with expect message type packet in
Dan Talaycoe226eb12010-02-18 23:06:30 -0800160
Dan Talayco6ce963a2010-03-07 21:58:13 -0800161 rc = delete_all_flows(self.controller, basic_logger)
162 self.assertEqual(rc, 0, "Failed to delete all flows")
163
Dan Talayco48370102010-03-03 15:17:33 -0800164 for of_port in basic_port_map.keys():
165 basic_logger.info("PKT IN test, port " + str(of_port))
Dan Talayco41eae8b2010-03-10 13:57:06 -0800166 pkt = simple_tcp_packet()
Dan Talaycodba244e2010-02-15 14:08:53 -0800167 self.dataplane.send(of_port, str(pkt))
Dan Talaycoe226eb12010-02-18 23:06:30 -0800168 #@todo Check for unexpected messages?
Dan Talayco2c0dba32010-03-06 22:47:06 -0800169 (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
Dan Talaycodba244e2010-02-15 14:08:53 -0800170
Dan Talaycoe226eb12010-02-18 23:06:30 -0800171 self.assertTrue(response is not None,
Dan Talayco6ce963a2010-03-07 21:58:13 -0800172 'Packet in message not received on port ' +
173 str(of_port))
Dan Talayco48370102010-03-03 15:17:33 -0800174 if str(pkt) != response.data:
Dan Talaycoef701f42010-05-07 09:22:35 -0700175 basic_logger.debug("pkt len " + str(len(str(pkt))) +
176 ": " + str(pkt))
177 basic_logger.debug("resp len " +
178 str(len(str(response.data))) +
179 ": " + str(response.data))
Dan Talayco48370102010-03-03 15:17:33 -0800180
181 self.assertEqual(str(pkt), response.data,
Dan Talayco6ce963a2010-03-07 21:58:13 -0800182 'Response packet does not match send packet' +
183 ' for port ' + str(of_port))
Dan Talaycodba244e2010-02-15 14:08:53 -0800184
Dan Talayco6ce963a2010-03-07 21:58:13 -0800185class PacketOut(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800186 """
187 Test packet out function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800188
189 Send packet out message to controller for each dataplane port and
190 verify the packet appears on the appropriate dataplane port
Dan Talaycodba244e2010-02-15 14:08:53 -0800191 """
192 def runTest(self):
193 # Construct packet to send to dataplane
194 # Send packet to dataplane
195 # Poll controller with expect message type packet in
Dan Talayco41eae8b2010-03-10 13:57:06 -0800196
197 rc = delete_all_flows(self.controller, basic_logger)
198 self.assertEqual(rc, 0, "Failed to delete all flows")
Dan Talaycodba244e2010-02-15 14:08:53 -0800199
200 # These will get put into function
Dan Talayco41eae8b2010-03-10 13:57:06 -0800201 outpkt = simple_tcp_packet()
Dan Talayco48370102010-03-03 15:17:33 -0800202 of_ports = basic_port_map.keys()
203 of_ports.sort()
204 for dp_port in of_ports:
Dan Talayco2c0dba32010-03-06 22:47:06 -0800205 msg = message.packet_out()
Dan Talayco48370102010-03-03 15:17:33 -0800206 msg.data = str(outpkt)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800207 act = action.action_output()
Dan Talayco48370102010-03-03 15:17:33 -0800208 act.port = dp_port
209 self.assertTrue(msg.actions.add(act), 'Could not add action to msg')
Dan Talaycodba244e2010-02-15 14:08:53 -0800210
Dan Talayco48370102010-03-03 15:17:33 -0800211 basic_logger.info("PacketOut to: " + str(dp_port))
212 rv = self.controller.message_send(msg)
213 self.assertTrue(rv == 0, "Error sending out message")
Dan Talaycodba244e2010-02-15 14:08:53 -0800214
Dan Talayco48370102010-03-03 15:17:33 -0800215 (of_port, pkt, pkt_time) = self.dataplane.poll(timeout=1)
Dan Talaycodba244e2010-02-15 14:08:53 -0800216
Dan Talayco48370102010-03-03 15:17:33 -0800217 self.assertTrue(pkt is not None, 'Packet not received')
218 basic_logger.info("PacketOut: got pkt from " + str(of_port))
219 if of_port is not None:
220 self.assertEqual(of_port, dp_port, "Unexpected receive port")
221 self.assertEqual(str(outpkt), str(pkt),
222 'Response packet does not match send packet')
Dan Talaycodba244e2010-02-15 14:08:53 -0800223
Dan Talayco6ce963a2010-03-07 21:58:13 -0800224class FlowStatsGet(SimpleProtocol):
225 """
226 Get stats
Dan Talayco2c0dba32010-03-06 22:47:06 -0800227
Dan Talayco6ce963a2010-03-07 21:58:13 -0800228 Simply verify stats get transaction
229 """
230 def runTest(self):
231 basic_logger.info("Running StatsGet")
Dan Talayco41eae8b2010-03-10 13:57:06 -0800232 basic_logger.info("Inserting trial flow")
233 request = message.flow_mod()
234 request.match.wildcards = ofp.OFPFW_ALL
235 request.buffer_id = 0xffffffff
236 rv = self.controller.message_send(request)
237 self.assertTrue(rv != -1, "Failed to insert test flow")
238
239 basic_logger.info("Sending flow request")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800240 request = message.flow_stats_request()
241 request.out_port = ofp.OFPP_NONE
Dan Talayco41eae8b2010-03-10 13:57:06 -0800242 request.table_id = 0xff
243 request.match.wildcards = 0 # ofp.OFPFW_ALL
Dan Talayco6ce963a2010-03-07 21:58:13 -0800244 response, pkt = self.controller.transact(request, timeout=2)
245 self.assertTrue(response is not None, "Did not get response")
Dan Talayco41eae8b2010-03-10 13:57:06 -0800246 basic_logger.info(response.show())
Dan Talayco6ce963a2010-03-07 21:58:13 -0800247
248class FlowMod(SimpleProtocol):
249 """
250 Insert a flow
251
252 Simple verification of a flow mod transaction
253 """
254
255 def runTest(self):
256 basic_logger.info("Running " + str(self))
257 request = message.flow_mod()
Dan Talayco6ce963a2010-03-07 21:58:13 -0800258 request.match.wildcards = ofp.OFPFW_ALL
259 request.buffer_id = 0xffffffff
260 rv = self.controller.message_send(request)
Dan Talayco41eae8b2010-03-10 13:57:06 -0800261 self.assertTrue(rv != -1, "Error installing flow mod")
262
Dan Talaycodba244e2010-02-15 14:08:53 -0800263if __name__ == "__main__":
Dan Talayco2c0dba32010-03-06 22:47:06 -0800264 print "Please run through oft script: ./oft --test_spec=basic"