blob: 268d313640ada742a540a53d4318963ef27f6ada [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
18from scapy.all import *
19import unittest
20
21import time
Dan Talayco710438c2010-02-18 15:16:07 -080022import signal
Dan Talaycodba244e2010-02-15 14:08:53 -080023import sys
Dan Talayco48370102010-03-03 15:17:33 -080024##@todo Use setup to place OFT modules in path
Dan Talaycodba244e2010-02-15 14:08:53 -080025sys.path.append("../src/python/oftest")
26from message import *
27from dataplane import *
28from controller import *
Dan Talayco48370102010-03-03 15:17:33 -080029import logging
Dan Talaycodba244e2010-02-15 14:08:53 -080030
Dan Talayco48370102010-03-03 15:17:33 -080031basic_port_map = None
32basic_logger = None
33basic_config = None
34
35def test_set_init(config):
36 """
37 Set up function for basic test classes
38
39 @param config The configuration dictionary; see oft
40 @return TestSuite object for the class; may depend on config
41 """
42
43 global basic_port_map
44 global basic_logger
45 global basic_config
46
47 basic_logger = logging.getLogger("basic")
48 basic_logger.info("Initializing test set")
49 basic_port_map = config["port_map"]
50 basic_config = config
51 return suite()
52
53def suite():
54 suite = unittest.TestSuite()
55 suite.addTest(SimpleProtocolTestCase())
56 suite.addTest(SimpleDataPlaneTestCase())
57 suite.addTest(EchoTestCase())
58 suite.addTest(EchoWithDataTestCase())
59 suite.addTest(PacketInTestCase())
60 suite.addTest(PacketOutTestCase())
61 return suite
Dan Talayco710438c2010-02-18 15:16:07 -080062
Dan Talaycodba244e2010-02-15 14:08:53 -080063class SimpleProtocolTestCase(unittest.TestCase):
64 """
65 Root class for setting up the controller
66 """
67
Dan Talayco710438c2010-02-18 15:16:07 -080068 def sig_handler(self):
Dan Talayco48370102010-03-03 15:17:33 -080069 basic_logger.critical("Received interrupt signal; exiting")
Dan Talayco710438c2010-02-18 15:16:07 -080070 print "Received interrupt signal; exiting"
71 self.controller.shutdown()
72 sys.exit(1)
73
Dan Talaycodba244e2010-02-15 14:08:53 -080074 def setUp(self):
Dan Talayco710438c2010-02-18 15:16:07 -080075 signal.signal(signal.SIGINT, self.sig_handler)
Dan Talayco48370102010-03-03 15:17:33 -080076 basic_logger.info("Setup for " + str(self))
77 self.controller = Controller(host=basic_config["controller_host"],
78 port=basic_config["controller_port"])
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 Talayco710438c2010-02-18 15:16:07 -080086 # self.controller.join()
Dan Talaycodba244e2010-02-15 14:08:53 -080087
88 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -080089 # Just a simple sanity check as illustration
Dan Talayco48370102010-03-03 15:17:33 -080090 basic_logger.info("Running simple proto test")
Dan Talayco710438c2010-02-18 15:16:07 -080091 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -080092 str(self) + 'No connection to switch')
93
94class SimpleDataPlaneTestCase(SimpleProtocolTestCase):
95 """
96 Root class that sets up the controller and dataplane
97 """
98 def setUp(self):
99 SimpleProtocolTestCase.setUp(self)
100 self.dataplane = DataPlane()
Dan Talayco48370102010-03-03 15:17:33 -0800101 for of_port, ifname in basic_port_map.items():
Dan Talaycodba244e2010-02-15 14:08:53 -0800102 self.dataplane.port_add(ifname, of_port)
103
104 def tearDown(self):
Dan Talayco48370102010-03-03 15:17:33 -0800105 basic_logger.info("Teardown for simple dataplane test")
Dan Talaycodba244e2010-02-15 14:08:53 -0800106 SimpleProtocolTestCase.tearDown(self)
Dan Talayco48370102010-03-03 15:17:33 -0800107 self.dataplane.kill(join_threads=True)
108 basic_logger.info("Teardown done")
Dan Talaycodba244e2010-02-15 14:08:53 -0800109
110 def runTest(self):
Dan Talayco710438c2010-02-18 15:16:07 -0800111 self.assertTrue(self.controller.switch_socket is not None,
Dan Talaycodba244e2010-02-15 14:08:53 -0800112 str(self) + 'No connection to switch')
113 # self.dataplane.show()
114 # Would like an assert that checks the data plane
115
116class EchoTestCase(SimpleProtocolTestCase):
117 """
118 Test echo response with no data
119 """
120 def runTest(self):
121 request = echo_request()
Dan Talaycoe226eb12010-02-18 23:06:30 -0800122 response, pkt = self.controller.transact(request)
Dan Talaycodba244e2010-02-15 14:08:53 -0800123 self.assertEqual(response.header.type, OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800124 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800125 self.assertEqual(request.header.xid, response.header.xid,
126 'response xid != request xid')
127 self.assertEqual(len(response.data), 0, 'response data non-empty')
128
129class EchoWithDataTestCase(SimpleProtocolTestCase):
130 """
131 Test echo response with short string data
132 """
133 def runTest(self):
134 request = echo_request()
135 request.data = 'OpenFlow Will Rule The World'
Dan Talaycoe226eb12010-02-18 23:06:30 -0800136 response, pkt = self.controller.transact(request)
Dan Talaycodba244e2010-02-15 14:08:53 -0800137 self.assertEqual(response.header.type, OFPT_ECHO_REPLY,
Dan Talaycoa92e75b2010-02-16 20:53:56 -0800138 'response is not echo_reply')
Dan Talaycodba244e2010-02-15 14:08:53 -0800139 self.assertEqual(request.header.xid, response.header.xid,
140 'response xid != request xid')
141 self.assertEqual(request.data, response.data,
142 'response data does not match request')
143
144class PacketInTestCase(SimpleDataPlaneTestCase):
145 """
146 Test packet in function
147 """
148 def runTest(self):
149 # Construct packet to send to dataplane
Dan Talaycoe226eb12010-02-18 23:06:30 -0800150 # Send packet to dataplane, once to each port
Dan Talaycodba244e2010-02-15 14:08:53 -0800151 # Poll controller with expect message type packet in
152 # For now, a random packet from scapy tutorial
Dan Talaycoe226eb12010-02-18 23:06:30 -0800153
Dan Talayco48370102010-03-03 15:17:33 -0800154 for of_port in basic_port_map.keys():
155 basic_logger.info("PKT IN test, port " + str(of_port))
Dan Talaycoe226eb12010-02-18 23:06:30 -0800156 pkt=Ether()/IP(dst="www.slashdot.org")/TCP()/\
157 ("GET /index.html HTTP/1.0. port" + str(of_port))
Dan Talaycodba244e2010-02-15 14:08:53 -0800158 self.dataplane.send(of_port, str(pkt))
Dan Talaycoe226eb12010-02-18 23:06:30 -0800159 #@todo Check for unexpected messages?
160 (response, raw) = self.controller.poll(OFPT_PACKET_IN, 2)
Dan Talaycodba244e2010-02-15 14:08:53 -0800161
Dan Talaycoe226eb12010-02-18 23:06:30 -0800162 self.assertTrue(response is not None,
163 'Packet in message not received')
Dan Talayco48370102010-03-03 15:17:33 -0800164 if str(pkt) != response.data:
165 basic_logger.debug("pkt: "+str(pkt)+" resp: " +
166 str(response))
167
168 self.assertEqual(str(pkt), response.data,
Dan Talaycoe226eb12010-02-18 23:06:30 -0800169 'Response packet does not match send packet')
Dan Talaycodba244e2010-02-15 14:08:53 -0800170
171class PacketOutTestCase(SimpleDataPlaneTestCase):
172 """
173 Test packet out function
174 """
175 def runTest(self):
176 # Construct packet to send to dataplane
177 # Send packet to dataplane
178 # Poll controller with expect message type packet in
179 # For now, a random packet from scapy tutorial
180
181 # These will get put into function
182 outpkt=Ether()/IP(dst="www.slashdot.org")/TCP()/\
183 "GET /index.html HTTP/1.0 \n\n"
Dan Talayco48370102010-03-03 15:17:33 -0800184 of_ports = basic_port_map.keys()
185 of_ports.sort()
186 for dp_port in of_ports:
187 msg = packet_out()
188 msg.data = str(outpkt)
189 act = action_output()
190 act.port = dp_port
191 self.assertTrue(msg.actions.add(act), 'Could not add action to msg')
Dan Talaycodba244e2010-02-15 14:08:53 -0800192
Dan Talayco48370102010-03-03 15:17:33 -0800193 basic_logger.info("PacketOut to: " + str(dp_port))
194 rv = self.controller.message_send(msg)
195 self.assertTrue(rv == 0, "Error sending out message")
Dan Talaycodba244e2010-02-15 14:08:53 -0800196
Dan Talayco48370102010-03-03 15:17:33 -0800197 (of_port, pkt, pkt_time) = self.dataplane.poll(timeout=1)
Dan Talaycodba244e2010-02-15 14:08:53 -0800198
Dan Talayco48370102010-03-03 15:17:33 -0800199 self.assertTrue(pkt is not None, 'Packet not received')
200 basic_logger.info("PacketOut: got pkt from " + str(of_port))
201 if of_port is not None:
202 self.assertEqual(of_port, dp_port, "Unexpected receive port")
203 self.assertEqual(str(outpkt), str(pkt),
204 'Response packet does not match send packet')
Dan Talaycodba244e2010-02-15 14:08:53 -0800205
206if __name__ == "__main__":
207 unittest.main()
Dan Talayco48370102010-03-03 15:17:33 -0800208
Dan Talayco710438c2010-02-18 15:16:07 -0800209# suite = unittest.TestLoader().loadTestsFromTestCase(PacketOutTestCase)
210# unittest.TextTestRunner(verbosity=2).run(suite)