blob: 37618ae2c23f2a828e190f088f47008ec1160e14 [file] [log] [blame]
Rich Lane1dee22c2013-06-13 15:50:28 -07001# Distributed under the OpenFlow Software License (see LICENSE)
2# Copyright (c) 2010 The Board of Trustees of The Leland Stanford Junior University
3# Copyright (c) 2012, 2013 Big Switch Networks, Inc.
4# Copyright (c) 2012, 2013 CPqD
5# Copyright (c) 2012, 2013 Ericsson
Rich Lane1ab6f832013-05-03 17:51:35 -07006"""
7Basic test cases
8
9Test cases in other modules depend on this functionality.
10"""
11
Rich Lane1dee22c2013-06-13 15:50:28 -070012import logging
13
Rich Lane1ab6f832013-05-03 17:51:35 -070014from oftest import config
15import oftest.base_tests as base_tests
16import ofp
17
18from oftest.testutils import *
19
20@group('smoke')
21class Echo(base_tests.SimpleProtocol):
22 """
23 Test echo response with no data
24 """
25 def runTest(self):
26 request = ofp.message.echo_request()
27 response, pkt = self.controller.transact(request)
28 self.assertTrue(response is not None,
29 "Did not get echo reply")
30 self.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
31 'response is not echo_reply')
32 self.assertEqual(request.xid, response.xid,
33 'response xid != request xid')
34 self.assertEqual(len(response.data), 0, 'response data non-empty')
Rich Lane1dee22c2013-06-13 15:50:28 -070035
36class EchoWithData(base_tests.SimpleProtocol):
37 """
38 Test echo response with short string data
39 """
40 def runTest(self):
41 data = 'OpenFlow Will Rule The World'
42 request = ofp.message.echo_request(data=data)
43 response, _ = self.controller.transact(request)
44 self.assertTrue(response is not None,
45 "Did not get echo reply")
46 self.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
47 'response is not echo_reply')
48 self.assertEqual(request.xid, response.xid,
49 'response xid != request xid')
50 self.assertEqual(request.data, response.data,
51 'response data != request data')
52
53class FeaturesRequest(base_tests.SimpleProtocol):
54 """
55 Test features_request to make sure we get a response
56
57 Does NOT test the contents; just that we get a response
58 """
59 def runTest(self):
60 request = ofp.message.features_request()
61 response,_ = self.controller.transact(request)
62 self.assertTrue(response is not None,
63 'Did not get features reply')
64
65class OutputExact(base_tests.SimpleDataPlane):
66 """
67 Test output function for an exact-match flow
68
69 For each port A, adds a flow directing matching packets to that port.
70 Then, for all other ports B, verifies that sending a matching packet
71 to B results in an output to A.
72 """
73 def runTest(self):
74 ports = sorted(config["port_map"].keys())
75
76 delete_all_flows(self.controller)
77
78 parsed_pkt = simple_tcp_packet()
79 pkt = str(parsed_pkt)
80 match = packet_to_flow_match(self, parsed_pkt)
81
82 for out_port in ports:
83 request = ofp.message.flow_add(
84 table_id=0,
85 cookie=42,
86 match=match,
87 instructions=[
88 ofp.instruction.apply_actions(
89 actions=[
90 ofp.action.output(
91 port=out_port,
92 max_len=ofp.OFPCML_NO_BUFFER)])],
93 buffer_id=0xffffffff,
94 priority=1000)
95
96 logging.info("Inserting flow sending matching packets to port %d", out_port)
97 self.controller.message_send(request)
98 do_barrier(self.controller)
99
100 for in_port in ports:
101 if in_port == out_port:
102 continue
103 logging.info("OutputExact test, ports %d to %d", in_port, out_port)
104 self.dataplane.send(in_port, pkt)
105 receive_pkt_verify(self, [out_port], pkt, in_port)
106
107class PacketInExact(base_tests.SimpleDataPlane):
108 """
109 Test packet in function for an exact-match flow
110
111 Send a packet to each dataplane port and verify that a packet
112 in message is received from the controller for each
113 """
114 def runTest(self):
115 delete_all_flows(self.controller)
116
117 parsed_pkt = simple_tcp_packet()
118 pkt = str(parsed_pkt)
119 match = packet_to_flow_match(self, parsed_pkt)
120
121 request = ofp.message.flow_add(
122 table_id=0,
123 cookie=42,
124 match=match,
125 instructions=[
126 ofp.instruction.apply_actions(
127 actions=[
128 ofp.action.output(
129 port=ofp.OFPP_CONTROLLER,
130 max_len=ofp.OFPCML_NO_BUFFER)])],
131 buffer_id=0xffffffff,
132 priority=1000)
133
134 logging.info("Inserting flow sending matching packets to controller")
135 self.controller.message_send(request)
136 do_barrier(self.controller)
137
138 for of_port in config["port_map"].keys():
139 logging.info("PacketInExact test, port %d", of_port)
140 self.dataplane.send(of_port, pkt)
141 verify_packet_in(self, pkt, of_port, ofp.OFPR_ACTION)
142
143class PacketInMiss(base_tests.SimpleDataPlane):
144 """
145 Test packet in function for a table-miss flow
146
147 Send a packet to each dataplane port and verify that a packet
148 in message is received from the controller for each
149 """
150 def runTest(self):
151 delete_all_flows(self.controller)
152
153 parsed_pkt = simple_tcp_packet()
154 pkt = str(parsed_pkt)
155
156 request = ofp.message.flow_add(
157 table_id=0,
158 cookie=42,
159 instructions=[
160 ofp.instruction.apply_actions(
161 actions=[
162 ofp.action.output(
163 port=ofp.OFPP_CONTROLLER,
164 max_len=ofp.OFPCML_NO_BUFFER)])],
165 buffer_id=0xffffffff,
166 priority=0)
167
168 logging.info("Inserting table-miss flow sending all packets to controller")
169 self.controller.message_send(request)
170 do_barrier(self.controller)
171
172 for of_port in config["port_map"].keys():
173 logging.info("PacketInMiss test, port %d", of_port)
174 self.dataplane.send(of_port, pkt)
175 verify_packet_in(self, pkt, of_port, ofp.OFPR_NO_MATCH)
176
177class PacketOut(base_tests.SimpleDataPlane):
178 """
179 Test packet out function
180
181 Send packet out message to controller for each dataplane port and
182 verify the packet appears on the appropriate dataplane port
183 """
184 def runTest(self):
185 pkt = str(simple_tcp_packet())
186
187 for of_port in config["port_map"].keys():
188 msg = ofp.message.packet_out(
189 in_port=ofp.OFPP_CONTROLLER,
190 actions=[ofp.action.output(port=of_port)],
191 buffer_id=ofp.OFP_NO_BUFFER,
192 data=pkt)
193
194 logging.info("PacketOut test, port %d", of_port)
195 self.controller.message_send(msg)
196 receive_pkt_verify(self, [of_port], pkt, ofp.OFPP_CONTROLLER)
197
198class FlowRemoveAll(base_tests.SimpleProtocol):
199 """
200 Remove all flows; required for almost all tests
201
202 Add a bunch of flows, remove them, and then make sure there are no flows left
203 This is an intentionally naive test to see if the baseline functionality works
204 and should be a precondition to any more complicated deletion test (e.g.,
205 delete_strict vs. delete)
206 """
207 def runTest(self):
208 for i in range(1,5):
209 logging.debug("Adding flow %d", i)
210 request = ofp.message.flow_add(
211 buffer_id=ofp.OFP_NO_BUFFER,
212 priority=i*1000)
213 self.controller.message_send(request)
214 do_barrier(self.controller)
215
216 delete_all_flows(self.controller)
217
218 logging.info("Sending flow stats request")
219 stats = get_flow_stats(self, ofp.match())
220 self.assertEqual(len(stats), 0, "Expected empty flow stats reply")
221
222
223## Multipart messages
224
225class DescStats(base_tests.SimpleProtocol):
226 """
227 Switch description multipart transaction
228
229 Only verifies we get a single reply.
230 """
231 def runTest(self):
232 request = ofp.message.desc_stats_request()
233 logging.info("Sending desc stats request")
234 response, _ = self.controller.transact(request)
235 self.assertTrue(response != None, "No response to desc stats request")
236 logging.info(response.show())
237 self.assertEquals(response.flags, 0, "Unexpected bit set in desc stats reply flags")
238
239class FlowStats(base_tests.SimpleProtocol):
240 """
241 Flow stats multipart transaction
242
243 Only verifies we get a reply.
244 """
245 def runTest(self):
246 logging.info("Sending flow stats request")
247 stats = get_flow_stats(self, ofp.match())
248 logging.info("Received %d flow stats entries", len(stats))
249 for entry in stats:
250 logging.info(entry.show())
251
252class AggregateStats(base_tests.SimpleProtocol):
253 """
254 Aggregate flow stats multipart transaction
255
256 Only verifies we get a single reply.
257 """
258 def runTest(self):
259 request = ofp.message.aggregate_stats_request(
260 table_id=ofp.OFPTT_ALL,
261 out_port=ofp.OFPP_ANY,
262 out_group=ofp.OFPG_ANY,
263 cookie=0,
264 cookie_mask=0)
265 logging.info("Sending aggregate flow stats request")
266 response, _ = self.controller.transact(request)
267 self.assertTrue(response != None, "No response to aggregate stats request")
268 logging.info(response.show())
269 self.assertEquals(response.flags, 0, "Unexpected bit set in aggregate stats reply flags")
270
271class TableStats(base_tests.SimpleProtocol):
272 """
273 Table stats multipart transaction
274
275 Only verifies we get a reply.
276 """
277 def runTest(self):
278 logging.info("Sending table stats request")
279 stats = get_stats(self, ofp.message.table_stats_request())
280 logging.info("Received %d table stats entries", len(stats))
281 for entry in stats:
282 logging.info(entry.show())
283
284class PortStats(base_tests.SimpleProtocol):
285 """
286 Port stats multipart transaction
287
288 Only verifies we get a reply.
289 """
290 def runTest(self):
291 request = ofp.message.port_stats_request(port_no=ofp.OFPP_ANY)
292 logging.info("Sending port stats request")
293 stats = get_stats(self, request)
294 logging.info("Received %d port stats entries", len(stats))
295 for entry in stats:
296 logging.info(entry.show())
297
298class QueueStats(base_tests.SimpleProtocol):
299 """
300 Queue stats multipart transaction
301
302 Only verifies we get a reply.
303 """
304 def runTest(self):
305 request = ofp.message.queue_stats_request(port_no=ofp.OFPP_ANY,
306 queue_id=ofp.OFPQ_ALL)
307 logging.info("Sending queue stats request")
308 stats = get_stats(self, request)
309 logging.info("Received %d queue stats entries", len(stats))
310 for entry in stats:
311 logging.info(entry.show())
312
313class GroupStats(base_tests.SimpleProtocol):
314 """
315 Group stats multipart transaction
316
317 Only verifies we get a reply.
318 """
319 def runTest(self):
320 request = ofp.message.group_stats_request(group_id=ofp.OFPG_ALL)
321 logging.info("Sending group stats request")
322 stats = get_stats(self, request)
323 logging.info("Received %d group stats entries", len(stats))
324 for entry in stats:
325 logging.info(entry.show())
326
327class GroupDescStats(base_tests.SimpleProtocol):
328 """
329 Group description multipart transaction
330
331 Only verifies we get a reply.
332 """
333 def runTest(self):
334 request = ofp.message.group_desc_stats_request()
335 logging.info("Sending group desc stats request")
336 stats = get_stats(self, request)
337 logging.info("Received %d group desc stats entries", len(stats))
338 for entry in stats:
339 logging.info(entry.show())
340
341class GroupFeaturesStats(base_tests.SimpleProtocol):
342 """
343 Group features multipart transaction
344
345 Only verifies we get a single reply.
346 """
347 def runTest(self):
348 request = ofp.message.group_features_stats_request()
349 logging.info("Sending group features stats request")
350 response, _ = self.controller.transact(request)
351 self.assertTrue(response != None, "No response to group features stats request")
352 logging.info(response.show())
353 self.assertEquals(response.flags, 0, "Unexpected bit set in group features stats reply flags")
354
355class MeterStats(base_tests.SimpleProtocol):
356 """
357 Meter stats multipart transaction
358
359 Only verifies we get a reply.
360 """
361 def runTest(self):
362 request = ofp.message.meter_stats_request(meter_id=ofp.OFPM_ALL)
363 logging.info("Sending meter stats request")
364 stats = get_stats(self, request)
365 logging.info("Received %d meter stats entries", len(stats))
366 for entry in stats:
367 logging.info(entry.show())
368
369class MeterConfigStats(base_tests.SimpleProtocol):
370 """
371 Meter config multipart transaction
372
373 Only verifies we get a reply.
374 """
375 def runTest(self):
376 request = ofp.message.meter_config_stats_request(meter_id=ofp.OFPM_ALL)
377 logging.info("Sending meter config stats request")
378 stats = get_stats(self, request)
379 logging.info("Received %d meter config stats entries", len(stats))
380 for entry in stats:
381 logging.info(entry.show())
382
383class MeterFeaturesStats(base_tests.SimpleProtocol):
384 """
385 Meter features multipart transaction
386
387 Only verifies we get a single reply.
388 """
389 def runTest(self):
390 request = ofp.message.meter_features_stats_request()
391 logging.info("Sending meter features stats request")
392 response, _ = self.controller.transact(request)
393 self.assertTrue(response != None, "No response to meter features stats request")
394 logging.info(response.show())
395 self.assertEquals(response.flags, 0, "Unexpected bit set in meter features stats reply flags")
396
397@disabled # pyloxi does not yet support table features
398class TableFeaturesStats(base_tests.SimpleProtocol):
399 """
400 Table features multipart transaction
401
402 Only verifies we get a reply.
403 """
404 def runTest(self):
405 logging.info("Sending table features stats request")
406 stats = get_stats(self, ofp.message.table_features_stats_request())
407 logging.info("Received %d table features stats entries", len(stats))
408 for entry in stats:
409 logging.info(entry.show())
410
411class PortDescStats(base_tests.SimpleProtocol):
412 """
413 Port description multipart transaction
414
415 Only verifies we get a reply.
416 """
417 def runTest(self):
418 logging.info("Sending port desc stats request")
419 stats = get_stats(self, ofp.message.port_desc_stats_request())
420 logging.info("Received %d port desc stats entries", len(stats))
421 for entry in stats:
422 logging.info(entry.show())
423
424class PortConfigMod(base_tests.SimpleProtocol):
425 """
426 Modify a bit in port config and verify changed
427
428 Get the switch configuration, modify the port configuration
429 and write it back; get the config again and verify changed.
430 Then set it back to the way it was.
431 """
432
433 def runTest(self):
434 logging.info("Running " + str(self))
435 for of_port, _ in config["port_map"].items(): # Grab first port
436 break
437
438 (_, config1, _) = \
439 port_config_get(self.controller, of_port)
440 self.assertTrue(config is not None, "Did not get port config")
441
442 logging.debug("No flood bit port " + str(of_port) + " is now " +
443 str(config1 & ofp.OFPPC_NO_PACKET_IN))
444
445 rv = port_config_set(self.controller, of_port,
446 config1 ^ ofp.OFPPC_NO_PACKET_IN,
447 ofp.OFPPC_NO_PACKET_IN)
448 self.assertTrue(rv != -1, "Error sending port mod")
449
450 # Verify change took place with same feature request
451 (_, config2, _) = port_config_get(self.controller, of_port)
452 self.assertTrue(config2 is not None, "Did not get port config2")
453 logging.debug("No packet_in bit port " + str(of_port) + " is now " +
454 str(config2 & ofp.OFPPC_NO_PACKET_IN))
455 self.assertTrue(config2 & ofp.OFPPC_NO_PACKET_IN !=
456 config1 & ofp.OFPPC_NO_PACKET_IN,
457 "Bit change did not take")
458 # Set it back
459 rv = port_config_set(self.controller, of_port, config1,
460 ofp.OFPPC_NO_PACKET_IN)
461 self.assertTrue(rv != -1, "Error sending port mod")
462
463class AsyncConfigGet(base_tests.SimpleProtocol):
464 """
465 Verify initial async config
466
467 Other tests rely on connections starting with these values.
468 """
469
470 def runTest(self):
471 logging.info("Sending get async config request")
472 response, _ = self.controller.transact(ofp.message.async_get_request())
473 self.assertTrue(response != None, "No response to get async config request")
474 logging.info(response.show())
475 self.assertEquals(response.packet_in_mask_equal_master & 0x07, 0x07)
476 self.assertEquals(response.port_status_mask_equal_master & 0x07, 0x07)
477 self.assertEquals(response.flow_removed_mask_equal_master & 0x0f, 0x0f)