blob: 00b1670b5e8db1502f769a44d2e4068c009dd197 [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 Talayco48370102010-03-03 15:17:33 -080032basic_port_map = None
33basic_logger = None
34basic_config = None
35
36def test_set_init(config):
37 """
38 Set up function for basic test classes
39
40 @param config The configuration dictionary; see oft
41 @return TestSuite object for the class; may depend on config
42 """
43
44 global basic_port_map
45 global basic_logger
46 global basic_config
47
48 basic_logger = logging.getLogger("basic")
49 basic_logger.info("Initializing test set")
50 basic_port_map = config["port_map"]
51 basic_config = config
Dan Talayco48370102010-03-03 15:17:33 -080052
Dan Talayco2c0dba32010-03-06 22:47:06 -080053# No longer used
54def suite(test_spec):
Dan Talayco48370102010-03-03 15:17:33 -080055 suite = unittest.TestSuite()
56 suite.addTest(SimpleProtocolTestCase())
57 suite.addTest(SimpleDataPlaneTestCase())
58 suite.addTest(EchoTestCase())
59 suite.addTest(EchoWithDataTestCase())
60 suite.addTest(PacketInTestCase())
61 suite.addTest(PacketOutTestCase())
62 return suite
Dan Talayco710438c2010-02-18 15:16:07 -080063
Dan Talaycodba244e2010-02-15 14:08:53 -080064class SimpleProtocolTestCase(unittest.TestCase):
65 """
66 Root class for setting up the controller
67 """
68
Dan Talayco710438c2010-02-18 15:16:07 -080069 def sig_handler(self):
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()
Dan Talayco710438c2010-02-18 15:16:07 -080074 sys.exit(1)
75
Dan Talaycodba244e2010-02-15 14:08:53 -080076 def setUp(self):
Dan Talayco710438c2010-02-18 15:16:07 -080077 signal.signal(signal.SIGINT, self.sig_handler)
Dan Talayco48370102010-03-03 15:17:33 -080078 basic_logger.info("Setup for " + str(self))
Dan Talayco2c0dba32010-03-06 22:47:06 -080079 self.controller = controller.Controller(
80 host=basic_config["controller_host"],
81 port=basic_config["controller_port"])
82 # clean_shutdown is set to False to force quit app
83 self.clean_shutdown = True
Dan Talayco710438c2010-02-18 15:16:07 -080084 self.controller.start()
85 self.controller.connect(timeout=20)
Dan Talayco48370102010-03-03 15:17:33 -080086 basic_logger.info("Connected " + str(self.controller.switch_addr))
Dan Talaycodba244e2010-02-15 14:08:53 -080087
88 def tearDown(self):
Dan Talayco48370102010-03-03 15:17:33 -080089 basic_logger.info("Teardown for simple proto test")
Dan Talaycodba244e2010-02-15 14:08:53 -080090 self.controller.shutdown()
Dan Talayco2c0dba32010-03-06 22:47:06 -080091 #@todo Review if join should be done on clean_shutdown
92 # if self.clean_shutdown:
93 # self.controller.join()
Dan Talaycodba244e2010-02-15 14:08:53 -080094
95 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -080096 # Just a simple sanity check as illustration
Dan Talayco48370102010-03-03 15:17:33 -080097 basic_logger.info("Running simple proto test")
Dan Talayco710438c2010-02-18 15:16:07 -080098 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -080099 str(self) + 'No connection to switch')
100
101class SimpleDataPlaneTestCase(SimpleProtocolTestCase):
102 """
103 Root class that sets up the controller and dataplane
104 """
105 def setUp(self):
106 SimpleProtocolTestCase.setUp(self)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800107 self.dataplane = dataplane.DataPlane()
Dan Talayco48370102010-03-03 15:17:33 -0800108 for of_port, ifname in basic_port_map.items():
Dan Talaycodba244e2010-02-15 14:08:53 -0800109 self.dataplane.port_add(ifname, of_port)
110
111 def tearDown(self):
Dan Talayco48370102010-03-03 15:17:33 -0800112 basic_logger.info("Teardown for simple dataplane test")
Dan Talaycodba244e2010-02-15 14:08:53 -0800113 SimpleProtocolTestCase.tearDown(self)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800114 self.dataplane.kill(join_threads=self.clean_shutdown)
Dan Talayco48370102010-03-03 15:17:33 -0800115 basic_logger.info("Teardown done")
Dan Talaycodba244e2010-02-15 14:08:53 -0800116
117 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -0800118 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800119 str(self) + 'No connection to switch')
120 # self.dataplane.show()
121 # Would like an assert that checks the data plane
122
123class EchoTestCase(SimpleProtocolTestCase):
124 """
125 Test echo response with no data
126 """
127 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800128 request = message.echo_request()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800129 response, pkt = self.controller.transact(request)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800130 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800131 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800132 self.assertEqual(request.header.xid, response.header.xid,
133 'response xid != request xid')
134 self.assertEqual(len(response.data), 0, 'response data non-empty')
135
136class EchoWithDataTestCase(SimpleProtocolTestCase):
137 """
138 Test echo response with short string data
139 """
140 def runTest(self):
Dan Talayco2c0dba32010-03-06 22:47:06 -0800141 request = message.echo_request()
Dan Talaycodba244e2010-02-15 14:08:53 -0800142 request.data = 'OpenFlow Will Rule The World'
Dan Talaycoe226eb12010-02-18 23:06:30 -0800143 response, pkt = self.controller.transact(request)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800144 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800145 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800146 self.assertEqual(request.header.xid, response.header.xid,
147 'response xid != request xid')
148 self.assertEqual(request.data, response.data,
149 'response data does not match request')
150
151class PacketInTestCase(SimpleDataPlaneTestCase):
152 """
153 Test packet in function
154 """
155 def runTest(self):
156 # Construct packet to send to dataplane
Dan Talaycoe226eb12010-02-18 23:06:30 -0800157 # Send packet to dataplane, once to each port
Dan Talaycodba244e2010-02-15 14:08:53 -0800158 # Poll controller with expect message type packet in
159 # For now, a random packet from scapy tutorial
Dan Talaycoe226eb12010-02-18 23:06:30 -0800160
Dan Talayco48370102010-03-03 15:17:33 -0800161 for of_port in basic_port_map.keys():
162 basic_logger.info("PKT IN test, port " + str(of_port))
Dan Talayco2c0dba32010-03-06 22:47:06 -0800163 pkt = scapy.Ether()/scapy.IP(dst="www.slashdot.org")/scapy.TCP()/\
Dan Talaycoe226eb12010-02-18 23:06:30 -0800164 ("GET /index.html HTTP/1.0. port" + str(of_port))
Dan Talaycodba244e2010-02-15 14:08:53 -0800165 self.dataplane.send(of_port, str(pkt))
Dan Talaycoe226eb12010-02-18 23:06:30 -0800166 #@todo Check for unexpected messages?
Dan Talayco2c0dba32010-03-06 22:47:06 -0800167 (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
Dan Talaycodba244e2010-02-15 14:08:53 -0800168
Dan Talaycoe226eb12010-02-18 23:06:30 -0800169 self.assertTrue(response is not None,
170 'Packet in message not received')
Dan Talayco48370102010-03-03 15:17:33 -0800171 if str(pkt) != response.data:
172 basic_logger.debug("pkt: "+str(pkt)+" resp: " +
173 str(response))
174
175 self.assertEqual(str(pkt), response.data,
Dan Talaycoe226eb12010-02-18 23:06:30 -0800176 'Response packet does not match send packet')
Dan Talaycodba244e2010-02-15 14:08:53 -0800177
178class PacketOutTestCase(SimpleDataPlaneTestCase):
179 """
180 Test packet out function
181 """
182 def runTest(self):
183 # Construct packet to send to dataplane
184 # Send packet to dataplane
185 # Poll controller with expect message type packet in
186 # For now, a random packet from scapy tutorial
187
188 # These will get put into function
Dan Talayco2c0dba32010-03-06 22:47:06 -0800189 outpkt = scapy.Ether()/scapy.IP(dst="www.slashdot.org")/scapy.TCP()/\
Dan Talaycodba244e2010-02-15 14:08:53 -0800190 "GET /index.html HTTP/1.0 \n\n"
Dan Talayco48370102010-03-03 15:17:33 -0800191 of_ports = basic_port_map.keys()
192 of_ports.sort()
193 for dp_port in of_ports:
Dan Talayco2c0dba32010-03-06 22:47:06 -0800194 msg = message.packet_out()
Dan Talayco48370102010-03-03 15:17:33 -0800195 msg.data = str(outpkt)
Dan Talayco2c0dba32010-03-06 22:47:06 -0800196 act = action.action_output()
Dan Talayco48370102010-03-03 15:17:33 -0800197 act.port = dp_port
198 self.assertTrue(msg.actions.add(act), 'Could not add action to msg')
Dan Talaycodba244e2010-02-15 14:08:53 -0800199
Dan Talayco48370102010-03-03 15:17:33 -0800200 basic_logger.info("PacketOut to: " + str(dp_port))
201 rv = self.controller.message_send(msg)
202 self.assertTrue(rv == 0, "Error sending out message")
Dan Talaycodba244e2010-02-15 14:08:53 -0800203
Dan Talayco48370102010-03-03 15:17:33 -0800204 (of_port, pkt, pkt_time) = self.dataplane.poll(timeout=1)
Dan Talaycodba244e2010-02-15 14:08:53 -0800205
Dan Talayco48370102010-03-03 15:17:33 -0800206 self.assertTrue(pkt is not None, 'Packet not received')
207 basic_logger.info("PacketOut: got pkt from " + str(of_port))
208 if of_port is not None:
209 self.assertEqual(of_port, dp_port, "Unexpected receive port")
210 self.assertEqual(str(outpkt), str(pkt),
211 'Response packet does not match send packet')
Dan Talaycodba244e2010-02-15 14:08:53 -0800212
Dan Talayco2c0dba32010-03-06 22:47:06 -0800213#class StatsGetTestCase(SimpleProtocolTestCase):
214# """
215# Get stats
216# """
217# def runTest(self):
218# request = message.flow_stats_request()
219# request.out_port = ofp.OFPP_NONE
220# request.match.wildcards = ofp.OFPFW_ALL
221# response, pkt = self.controller.transact(request)
222# response.show()
223
Dan Talaycodba244e2010-02-15 14:08:53 -0800224if __name__ == "__main__":
Dan Talayco2c0dba32010-03-06 22:47:06 -0800225 print "Please run through oft script: ./oft --test_spec=basic"
226
227#@todo Set up direct execution as script
228#if __name__ == "__main__":
229# TODO set up some config struct
230# test_set_init(config)
231# unittest.main()
Dan Talayco48370102010-03-03 15:17:33 -0800232
Dan Talayco710438c2010-02-18 15:16:07 -0800233# suite = unittest.TestLoader().loadTestsFromTestCase(PacketOutTestCase)
234# unittest.TextTestRunner(verbosity=2).run(suite)