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