blob: 2fd7fb979ab27a9a94ae2ea6af392379996d358e [file] [log] [blame]
Rich Lane629393f2013-01-10 15:37:33 -08001"""
2Basic protocol and dataplane test cases
3
4It is recommended that these definitions be kept in their own
5namespace as different groups of tests will likely define
6similar identifiers.
7"""
8
9import sys
10import logging
Rich Lane629393f2013-01-10 15:37:33 -080011import unittest
Rich Lane02eb6b02013-01-11 08:08:37 -080012import ipaddr
Rich Lane629393f2013-01-10 15:37:33 -080013
14from oftest import config
Rich Lane02eb6b02013-01-11 08:08:37 -080015import ofp
Rich Lane629393f2013-01-10 15:37:33 -080016import oftest.base_tests as base_tests
Rich Lane629393f2013-01-10 15:37:33 -080017import oftest.oft12.testutils as testutils
Rich Lane629393f2013-01-10 15:37:33 -080018
19class Echo(base_tests.SimpleProtocol):
20 """
21 Test echo response with no data
22 """
23 def runTest(self):
24 testutils.do_echo_request_reply_test(self, self.controller)
25
26class EchoWithData(base_tests.SimpleProtocol):
27 """
28 Test echo response with short string data
29 """
30 def runTest(self):
Rich Lane02eb6b02013-01-11 08:08:37 -080031 request = ofp.message.echo_request()
Rich Lane629393f2013-01-10 15:37:33 -080032 request.data = 'OpenFlow Will Rule The World'
33 response, _ = self.controller.transact(request)
34 self.assertEqual(response.header.type, ofp.OFPT_ECHO_REPLY,
35 'response is not echo_reply')
36 self.assertEqual(request.header.xid, response.header.xid,
37 'response xid != request xid')
38 self.assertEqual(request.data, response.data,
39 'response data does not match request')
40
41class FeaturesRequest(base_tests.SimpleProtocol):
42 """
43 Test features_request to make sure we get a response
44
45 Does NOT test the contents; just that we get a response
46 """
47 def runTest(self):
Rich Lane02eb6b02013-01-11 08:08:37 -080048 request = ofp.message.features_request()
Rich Lane629393f2013-01-10 15:37:33 -080049 response,_ = self.controller.transact(request)
50 self.assertTrue(response,"Got no features_reply to features_request")
51 self.assertEqual(response.header.type, ofp.OFPT_FEATURES_REPLY,
52 'response is not echo_reply')
53 self.assertTrue(len(response) >= 32, "features_reply too short: %d < 32 " % len(response))
54
55class PacketIn(base_tests.SimpleDataPlane):
56 """
57 Test packet in function
58
59 Send a packet to each dataplane port and verify that a packet
60 in message is received from the controller for each
61 """
62 def runTest(self):
63 # Construct packet to send to dataplane
64 # Send packet to dataplane, once to each port
65 # Poll controller with expect message type packet in
66
67 rc = testutils.delete_all_flows(self.controller, logging)
68 self.assertEqual(rc, 0, "Failed to delete all flows")
69
70 for of_port in config["port_map"].keys():
71 logging.info("PKT IN test, port " + str(of_port))
72 pkt = testutils.simple_tcp_packet()
73 self.dataplane.send(of_port, str(pkt))
74 #@todo Check for unexpected messages?
75 (response, _) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
76
77 self.assertTrue(response is not None,
78 'Packet in message not received on port ' +
79 str(of_port))
80 if str(pkt) != response.data:
81 logging.debug("pkt len " + str(len(str(pkt))) +
82 ": " + str(pkt))
83 logging.debug("resp len " +
84 str(len(str(response.data))) +
85 ": " + str(response.data))
86
87 self.assertEqual(str(pkt), response.data,
88 'Response packet does not match send packet' +
89 ' for port ' + str(of_port))
90
91class PacketOut(base_tests.SimpleDataPlane):
92 """
93 Test packet out function
94
95 Send packet out message to controller for each dataplane port and
96 verify the packet appears on the appropriate dataplane port
97 """
98 def runTest(self):
99 # Construct packet to send to dataplane
100 # Send packet to dataplane
101 # Poll controller with expect message type packet in
102
103 rc = testutils.delete_all_flows(self.controller, logging)
104 self.assertEqual(rc, 0, "Failed to delete all flows")
105
106 # These will get put into function
107 outpkt = testutils.simple_tcp_packet()
108 of_ports = config["port_map"].keys()
109 of_ports.sort()
110 for dp_port in of_ports:
Rich Lane02eb6b02013-01-11 08:08:37 -0800111 msg = ofp.message.packet_out()
Rich Lane629393f2013-01-10 15:37:33 -0800112 msg.in_port = ofp.OFPP_CONTROLLER
113 msg.data = str(outpkt)
Rich Lane02eb6b02013-01-11 08:08:37 -0800114 act = ofp.action.action_output()
Rich Lane629393f2013-01-10 15:37:33 -0800115 act.port = dp_port
116 self.assertTrue(msg.actions.add(act), 'Could not add action to msg')
117
118 logging.info("PacketOut to: " + str(dp_port))
119 rv = self.controller.message_send(msg)
120 self.assertTrue(rv == 0, "Error sending out message")
121
122 (of_port, pkt, _) = self.dataplane.poll(timeout=1)
123
124 self.assertTrue(pkt is not None, 'Packet not received')
125 logging.info("PacketOut: got pkt from " + str(of_port))
126 if of_port is not None:
127 self.assertEqual(of_port, dp_port, "Unexpected receive port")
128 self.assertEqual(str(outpkt), str(pkt),
129 'Response packet does not match send packet')
130
131class FlowRemoveAll(base_tests.SimpleProtocol):
132 """
133 Remove all flows; required for almost all tests
134
135 Add a bunch of flows, remove them, and then make sure there are no flows left
136 This is an intentionally naive test to see if the baseline functionality works
137 and should be a precondition to any more complicated deletion test (e.g.,
138 delete_strict vs. delete)
139 """
140 def runTest(self):
141 logging.info("Running StatsGet")
142 logging.info("Inserting trial flow")
Rich Lane02eb6b02013-01-11 08:08:37 -0800143 request = ofp.message.flow_mod()
Rich Lane629393f2013-01-10 15:37:33 -0800144 request.buffer_id = 0xffffffff
145 for i in range(1,5):
146 request.priority = i*1000
147 logging.debug("Adding flow %d" % i)
148 rv = self.controller.message_send(request)
149 self.assertTrue(rv != -1, "Failed to insert test flow %d" % i)
150 logging.info("Removing all flows")
151 testutils.delete_all_flows(self.controller, logging)
152 logging.info("Sending flow request")
Rich Lane02eb6b02013-01-11 08:08:37 -0800153 request = ofp.message.flow_stats_request()
Rich Lane629393f2013-01-10 15:37:33 -0800154 request.out_port = ofp.OFPP_ANY
155 request.out_group = ofp.OFPG_ANY
156 request.table_id = 0xff
157 response, _ = self.controller.transact(request, timeout=2)
158 self.assertTrue(response is not None, "Did not get response")
Rich Lane02eb6b02013-01-11 08:08:37 -0800159 self.assertTrue(isinstance(response,ofp.message.flow_stats_reply),"Not a flow_stats_reply")
Rich Lane629393f2013-01-10 15:37:33 -0800160 self.assertEqual(len(response.stats),0)
161 logging.debug(response.show())
162
163
164
165class FlowStatsGet(base_tests.SimpleProtocol):
166 """
167 Get stats
168
169 Simply verify stats get transaction
170 """
171 def runTest(self):
172 logging.info("Running StatsGet")
173 logging.info("Inserting trial flow")
Rich Lane02eb6b02013-01-11 08:08:37 -0800174 request = ofp.message.flow_mod()
Rich Lane629393f2013-01-10 15:37:33 -0800175 request.buffer_id = 0xffffffff
176 rv = self.controller.message_send(request)
177 self.assertTrue(rv != -1, "Failed to insert test flow")
178
179 logging.info("Sending flow request")
180 response = testutils.flow_stats_get(self)
181 logging.debug(response.show())
182
183class TableStatsGet(base_tests.SimpleProtocol):
184 """
185 Get table stats
186
187 Naively verify that we get a reply
188 do better sanity check of data in stats.TableStats test
189 """
190 def runTest(self):
191 logging.info("Running TableStatsGet")
192 logging.info("Sending table stats request")
Rich Lane02eb6b02013-01-11 08:08:37 -0800193 request = ofp.message.table_stats_request()
Rich Lane629393f2013-01-10 15:37:33 -0800194 response, _ = self.controller.transact(request, timeout=2)
195 self.assertTrue(response is not None, "Did not get response")
196 logging.debug(response.show())
197
198class FlowMod(base_tests.SimpleProtocol):
199 """
200 Insert a flow
201
202 Simple verification of a flow mod transaction
203 """
204
205 def runTest(self):
206 logging.info("Running " + str(self))
Rich Lane02eb6b02013-01-11 08:08:37 -0800207 request = ofp.message.flow_mod()
Rich Lane629393f2013-01-10 15:37:33 -0800208 request.buffer_id = 0xffffffff
209 rv = self.controller.message_send(request)
210 self.assertTrue(rv != -1, "Error installing flow mod")
211
212class PortConfigMod(base_tests.SimpleProtocol):
213 """
214 Modify a bit in port config and verify changed
215
216 Get the switch configuration, modify the port configuration
217 and write it back; get the config again and verify changed.
218 Then set it back to the way it was.
219 """
220
221 def runTest(self):
222 logging.info("Running " + str(self))
223 for of_port, _ in config["port_map"].items(): # Grab first port
224 break
225
226 (_, port_config, _) = \
227 testutils.port_config_get(self.controller, of_port, logging)
228 self.assertTrue(port_config is not None, "Did not get port config")
229
230 logging.debug("No flood bit port " + str(of_port) + " is now " +
231 str(port_config & ofp.OFPPC_NO_PACKET_IN))
232
233 rv = testutils.port_config_set(self.controller, of_port,
234 port_config ^ ofp.OFPPC_NO_PACKET_IN, ofp.OFPPC_NO_PACKET_IN,
235 logging)
236 self.assertTrue(rv != -1, "Error sending port mod")
237
238 # Verify change took place with same feature request
239 (_, port_config2, _) = \
240 testutils.port_config_get(self.controller, of_port, logging)
241 logging.debug("No packet_in bit port " + str(of_port) + " is now " +
242 str(port_config2 & ofp.OFPPC_NO_PACKET_IN))
243 self.assertTrue(port_config2 is not None, "Did not get port config2")
244 self.assertTrue(port_config2 & ofp.OFPPC_NO_PACKET_IN !=
245 port_config & ofp.OFPPC_NO_PACKET_IN,
246 "Bit change did not take")
247 # Set it back
248 rv = testutils.port_config_set(self.controller, of_port, port_config,
249 ofp.OFPPC_NO_PACKET_IN, logging)
250 self.assertTrue(rv != -1, "Error sending port mod")
251
252class TableModConfig(base_tests.SimpleProtocol):
253 """ Simple table modification
254
255 Mostly to make sure the switch correctly responds to these messages.
256 More complicated tests in the multi-tables.py tests
257 """
258 def runTest(self):
259 logging.info("Running " + str(self))
Rich Lane02eb6b02013-01-11 08:08:37 -0800260 table_mod = ofp.message.table_mod()
Rich Lane629393f2013-01-10 15:37:33 -0800261 table_mod.table_id = 0 # first table should always exist
262 table_mod.config = ofp.OFPTC_TABLE_MISS_CONTROLLER
263
264 rv = self.controller.message_send(table_mod)
265 self.assertTrue(rv != -1, "Error sending table_mod")
266 testutils.do_echo_request_reply_test(self, self.controller)
267
268
269if __name__ == "__main__":
270 print "Please run through oft script: ./oft --test_spec=basic"