blob: 820a693245aba490deaaea79cc3dfbbdb5095ea5 [file] [log] [blame]
Dan Talaycodba244e2010-02-15 14:08:53 -08001"""
2Basic test cases for the oftest OpenFlow test framework
3
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
10 The oftest framework source is in ../src/python/oftest
11 Configuration of the platform and system is stored in oft_config in that dir
12 The switch is actively attempting to contact the controller at the address
13indicated oin oft_config
14
Dan Talayco48370102010-03-03 15:17:33 -080015
Dan Talaycodba244e2010-02-15 14:08:53 -080016"""
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 scapy.all as scapy
24import unittest
25
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
42def test_set_init(config):
43 """
44 Set up function for basic test classes
45
46 @param config The configuration dictionary; see oft
47 @return TestSuite object for the class; may depend on config
48 """
49
50 global basic_port_map
51 global basic_logger
52 global basic_config
53
54 basic_logger = logging.getLogger("basic")
55 basic_logger.info("Initializing test set")
56 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 Talayco710438c2010-02-18 15:16:07 -080064 def sig_handler(self):
Dan Talayco48370102010-03-03 15:17:33 -080065 basic_logger.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()
Dan Talayco710438c2010-02-18 15:16:07 -080069 sys.exit(1)
70
Dan Talaycodba244e2010-02-15 14:08:53 -080071 def setUp(self):
Dan Talayco710438c2010-02-18 15:16:07 -080072 signal.signal(signal.SIGINT, self.sig_handler)
Dan Talayco48370102010-03-03 15:17:33 -080073 basic_logger.info("Setup for " + str(self))
Dan Talayco2c0dba32010-03-06 22:47:06 -080074 self.controller = controller.Controller(
75 host=basic_config["controller_host"],
76 port=basic_config["controller_port"])
77 # clean_shutdown is set to False to force quit app
78 self.clean_shutdown = True
Dan Talayco710438c2010-02-18 15:16:07 -080079 self.controller.start()
80 self.controller.connect(timeout=20)
Dan Talayco48370102010-03-03 15:17:33 -080081 basic_logger.info("Connected " + str(self.controller.switch_addr))
Dan Talaycodba244e2010-02-15 14:08:53 -080082
83 def tearDown(self):
Dan Talayco48370102010-03-03 15:17:33 -080084 basic_logger.info("Teardown for simple proto test")
Dan Talaycodba244e2010-02-15 14:08:53 -080085 self.controller.shutdown()
Dan Talayco2c0dba32010-03-06 22:47:06 -080086 #@todo Review if join should be done on clean_shutdown
87 # if self.clean_shutdown:
88 # self.controller.join()
Dan Talaycodba244e2010-02-15 14:08:53 -080089
90 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -080091 # Just a simple sanity check as illustration
Dan Talayco48370102010-03-03 15:17:33 -080092 basic_logger.info("Running simple proto test")
Dan Talayco710438c2010-02-18 15:16:07 -080093 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -080094 str(self) + 'No connection to switch')
95
Dan Talayco6ce963a2010-03-07 21:58:13 -080096class SimpleDataPlane(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -080097 """
98 Root class that sets up the controller and dataplane
99 """
100 def setUp(self):
Dan Talayco6ce963a2010-03-07 21:58:13 -0800101 SimpleProtocol.setUp(self)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800102 self.dataplane = dataplane.DataPlane()
Dan Talayco48370102010-03-03 15:17:33 -0800103 for of_port, ifname in basic_port_map.items():
Dan Talaycodba244e2010-02-15 14:08:53 -0800104 self.dataplane.port_add(ifname, of_port)
105
106 def tearDown(self):
Dan Talayco48370102010-03-03 15:17:33 -0800107 basic_logger.info("Teardown for simple dataplane test")
Dan Talayco6ce963a2010-03-07 21:58:13 -0800108 SimpleProtocol.tearDown(self)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800109 self.dataplane.kill(join_threads=self.clean_shutdown)
Dan Talayco48370102010-03-03 15:17:33 -0800110 basic_logger.info("Teardown done")
Dan Talaycodba244e2010-02-15 14:08:53 -0800111
112 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -0800113 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800114 str(self) + 'No connection to switch')
115 # self.dataplane.show()
116 # Would like an assert that checks the data plane
117
Dan Talayco6ce963a2010-03-07 21:58:13 -0800118class Echo(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800119 """
120 Test echo response with no data
121 """
122 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800123 request = message.echo_request()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800124 response, pkt = self.controller.transact(request)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800125 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800126 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800127 self.assertEqual(request.header.xid, response.header.xid,
128 'response xid != request xid')
129 self.assertEqual(len(response.data), 0, 'response data non-empty')
130
Dan Talayco6ce963a2010-03-07 21:58:13 -0800131class EchoWithData(SimpleProtocol):
Dan Talaycodba244e2010-02-15 14:08:53 -0800132 """
133 Test echo response with short string data
134 """
135 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800136 request = message.echo_request()
Dan Talaycodba244e2010-02-15 14:08:53 -0800137 request.data = 'OpenFlow Will Rule The World'
Dan Talaycoe226eb12010-02-18 23:06:30 -0800138 response, pkt = self.controller.transact(request)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800139 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800140 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800141 self.assertEqual(request.header.xid, response.header.xid,
142 'response xid != request xid')
143 self.assertEqual(request.data, response.data,
144 'response data does not match request')
145
Dan Talayco6ce963a2010-03-07 21:58:13 -0800146class PacketIn(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800147 """
148 Test packet in function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800149
150 Send a packet to each dataplane port and verify that a packet
151 in message is received from the controller for each
Dan Talaycodba244e2010-02-15 14:08:53 -0800152 """
153 def runTest(self):
154 # Construct packet to send to dataplane
Dan Talaycoe226eb12010-02-18 23:06:30 -0800155 # Send packet to dataplane, once to each port
Dan Talaycodba244e2010-02-15 14:08:53 -0800156 # Poll controller with expect message type packet in
157 # For now, a random packet from scapy tutorial
Dan Talaycoe226eb12010-02-18 23:06:30 -0800158
Dan Talayco6ce963a2010-03-07 21:58:13 -0800159 rc = delete_all_flows(self.controller, basic_logger)
160 self.assertEqual(rc, 0, "Failed to delete all flows")
161
Dan Talayco48370102010-03-03 15:17:33 -0800162 for of_port in basic_port_map.keys():
163 basic_logger.info("PKT IN test, port " + str(of_port))
Dan Talayco2c0dba32010-03-06 22:47:06 -0800164 pkt = scapy.Ether()/scapy.IP(dst="www.slashdot.org")/scapy.TCP()/\
Dan Talaycoe226eb12010-02-18 23:06:30 -0800165 ("GET /index.html HTTP/1.0. port" + str(of_port))
Dan Talaycodba244e2010-02-15 14:08:53 -0800166 self.dataplane.send(of_port, str(pkt))
Dan Talaycoe226eb12010-02-18 23:06:30 -0800167 #@todo Check for unexpected messages?
Dan Talayco2c0dba32010-03-06 22:47:06 -0800168 (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
Dan Talaycodba244e2010-02-15 14:08:53 -0800169
Dan Talaycoe226eb12010-02-18 23:06:30 -0800170 self.assertTrue(response is not None,
Dan Talayco6ce963a2010-03-07 21:58:13 -0800171 'Packet in message not received on port ' +
172 str(of_port))
Dan Talayco48370102010-03-03 15:17:33 -0800173 if str(pkt) != response.data:
174 basic_logger.debug("pkt: "+str(pkt)+" resp: " +
175 str(response))
176
177 self.assertEqual(str(pkt), response.data,
Dan Talayco6ce963a2010-03-07 21:58:13 -0800178 'Response packet does not match send packet' +
179 ' for port ' + str(of_port))
Dan Talaycodba244e2010-02-15 14:08:53 -0800180
Dan Talayco6ce963a2010-03-07 21:58:13 -0800181class PacketOut(SimpleDataPlane):
Dan Talaycodba244e2010-02-15 14:08:53 -0800182 """
183 Test packet out function
Dan Talayco6ce963a2010-03-07 21:58:13 -0800184
185 Send packet out message to controller for each dataplane port and
186 verify the packet appears on the appropriate dataplane port
Dan Talaycodba244e2010-02-15 14:08:53 -0800187 """
188 def runTest(self):
189 # Construct packet to send to dataplane
190 # Send packet to dataplane
191 # Poll controller with expect message type packet in
192 # For now, a random packet from scapy tutorial
193
194 # These will get put into function
Dan Talayco2c0dba32010-03-06 22:47:06 -0800195 outpkt = scapy.Ether()/scapy.IP(dst="www.slashdot.org")/scapy.TCP()/\
Dan Talaycodba244e2010-02-15 14:08:53 -0800196 "GET /index.html HTTP/1.0 \n\n"
Dan Talayco48370102010-03-03 15:17:33 -0800197 of_ports = basic_port_map.keys()
198 of_ports.sort()
199 for dp_port in of_ports:
Dan Talayco2c0dba32010-03-06 22:47:06 -0800200 msg = message.packet_out()
Dan Talayco48370102010-03-03 15:17:33 -0800201 msg.data = str(outpkt)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800202 act = action.action_output()
Dan Talayco48370102010-03-03 15:17:33 -0800203 act.port = dp_port
204 self.assertTrue(msg.actions.add(act), 'Could not add action to msg')
Dan Talaycodba244e2010-02-15 14:08:53 -0800205
Dan Talayco48370102010-03-03 15:17:33 -0800206 basic_logger.info("PacketOut to: " + str(dp_port))
207 rv = self.controller.message_send(msg)
208 self.assertTrue(rv == 0, "Error sending out message")
Dan Talaycodba244e2010-02-15 14:08:53 -0800209
Dan Talayco48370102010-03-03 15:17:33 -0800210 (of_port, pkt, pkt_time) = self.dataplane.poll(timeout=1)
Dan Talaycodba244e2010-02-15 14:08:53 -0800211
Dan Talayco48370102010-03-03 15:17:33 -0800212 self.assertTrue(pkt is not None, 'Packet not received')
213 basic_logger.info("PacketOut: got pkt from " + str(of_port))
214 if of_port is not None:
215 self.assertEqual(of_port, dp_port, "Unexpected receive port")
216 self.assertEqual(str(outpkt), str(pkt),
217 'Response packet does not match send packet')
Dan Talaycodba244e2010-02-15 14:08:53 -0800218
Dan Talayco6ce963a2010-03-07 21:58:13 -0800219class FlowStatsGet(SimpleProtocol):
220 """
221 Get stats
Dan Talayco2c0dba32010-03-06 22:47:06 -0800222
Dan Talayco6ce963a2010-03-07 21:58:13 -0800223 Simply verify stats get transaction
224 """
225 def runTest(self):
226 basic_logger.info("Running StatsGet")
227 request = message.flow_stats_request()
228 request.out_port = ofp.OFPP_NONE
229 request.match.wildcards = ofp.OFPFW_ALL
230 response, pkt = self.controller.transact(request, timeout=2)
231 self.assertTrue(response is not None, "Did not get response")
232
233class FlowMod(SimpleProtocol):
234 """
235 Insert a flow
236
237 Simple verification of a flow mod transaction
238 """
239
240 def runTest(self):
241 basic_logger.info("Running " + str(self))
242 request = message.flow_mod()
243 of_ports = basic_port_map.keys()
244 of_ports.sort()
245 request.out_port = of_ports[0]
246 request.match.wildcards = ofp.OFPFW_ALL
247 request.buffer_id = 0xffffffff
248 rv = self.controller.message_send(request)
249 self.assertTrue(rv != -1, "Did not get response")
250
Dan Talaycodba244e2010-02-15 14:08:53 -0800251if __name__ == "__main__":
Dan Talayco2c0dba32010-03-06 22:47:06 -0800252 print "Please run through oft script: ./oft --test_spec=basic"