blob: eeedec7d5867523e9f808b8aa9e5bbbb2a0d16d8 [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
Rich Lane82c882d2013-08-09 17:13:52 -070065class DefaultDrop(base_tests.SimpleDataPlane):
66 """
67 Check that an empty flowtable results in drops
68 """
69 def runTest(self):
70 in_port, = openflow_ports(1)
71 delete_all_flows(self.controller)
72
73 pkt = str(simple_tcp_packet())
74 self.dataplane.send(in_port, pkt)
75 verify_no_packet_in(self, pkt, None)
76 receive_pkt_check(self.dataplane, pkt, [], openflow_ports(), self)
77
Rich Lane1dee22c2013-06-13 15:50:28 -070078class OutputExact(base_tests.SimpleDataPlane):
79 """
80 Test output function for an exact-match flow
81
82 For each port A, adds a flow directing matching packets to that port.
Rich Laneb80c1302013-07-11 19:32:24 -070083 Then, for all other ports B != A, verifies that sending a matching packet
Rich Lane1dee22c2013-06-13 15:50:28 -070084 to B results in an output to A.
85 """
86 def runTest(self):
87 ports = sorted(config["port_map"].keys())
88
89 delete_all_flows(self.controller)
90
91 parsed_pkt = simple_tcp_packet()
92 pkt = str(parsed_pkt)
93 match = packet_to_flow_match(self, parsed_pkt)
94
95 for out_port in ports:
96 request = ofp.message.flow_add(
97 table_id=0,
98 cookie=42,
99 match=match,
100 instructions=[
101 ofp.instruction.apply_actions(
102 actions=[
103 ofp.action.output(
104 port=out_port,
105 max_len=ofp.OFPCML_NO_BUFFER)])],
Rich Laneb80c1302013-07-11 19:32:24 -0700106 buffer_id=ofp.OFP_NO_BUFFER,
Rich Lane1dee22c2013-06-13 15:50:28 -0700107 priority=1000)
108
109 logging.info("Inserting flow sending matching packets to port %d", out_port)
110 self.controller.message_send(request)
111 do_barrier(self.controller)
112
113 for in_port in ports:
114 if in_port == out_port:
115 continue
116 logging.info("OutputExact test, ports %d to %d", in_port, out_port)
117 self.dataplane.send(in_port, pkt)
118 receive_pkt_verify(self, [out_port], pkt, in_port)
119
Rich Lane692b9c02013-07-15 15:38:50 -0700120class OutputWildcard(base_tests.SimpleDataPlane):
121 """
122 Test output function for a match-all (but not table-miss) flow
123
124 For each port A, adds a flow directing all packets to that port.
125 Then, for all other ports B != A, verifies that sending a packet
126 to B results in an output to A.
127 """
128 def runTest(self):
129 ports = sorted(config["port_map"].keys())
130
131 delete_all_flows(self.controller)
132
133 pkt = str(simple_tcp_packet())
134
135 for out_port in ports:
136 request = ofp.message.flow_add(
137 table_id=0,
138 cookie=42,
139 instructions=[
140 ofp.instruction.apply_actions(
141 actions=[
142 ofp.action.output(
143 port=out_port,
144 max_len=ofp.OFPCML_NO_BUFFER)])],
145 buffer_id=ofp.OFP_NO_BUFFER,
146 priority=1000)
147
148 logging.info("Inserting flow sending all packets to port %d", out_port)
149 self.controller.message_send(request)
150 do_barrier(self.controller)
151
152 for in_port in ports:
153 if in_port == out_port:
154 continue
155 logging.info("OutputWildcard test, ports %d to %d", in_port, out_port)
156 self.dataplane.send(in_port, pkt)
157 receive_pkt_verify(self, [out_port], pkt, in_port)
158
Rich Lane1dee22c2013-06-13 15:50:28 -0700159class PacketInExact(base_tests.SimpleDataPlane):
160 """
161 Test packet in function for an exact-match flow
162
163 Send a packet to each dataplane port and verify that a packet
164 in message is received from the controller for each
165 """
166 def runTest(self):
167 delete_all_flows(self.controller)
168
169 parsed_pkt = simple_tcp_packet()
170 pkt = str(parsed_pkt)
171 match = packet_to_flow_match(self, parsed_pkt)
172
173 request = ofp.message.flow_add(
174 table_id=0,
175 cookie=42,
176 match=match,
177 instructions=[
178 ofp.instruction.apply_actions(
179 actions=[
180 ofp.action.output(
181 port=ofp.OFPP_CONTROLLER,
182 max_len=ofp.OFPCML_NO_BUFFER)])],
Rich Lane682db962013-07-11 20:19:27 -0700183 buffer_id=ofp.OFP_NO_BUFFER,
Rich Lane1dee22c2013-06-13 15:50:28 -0700184 priority=1000)
185
186 logging.info("Inserting flow sending matching packets to controller")
187 self.controller.message_send(request)
188 do_barrier(self.controller)
189
190 for of_port in config["port_map"].keys():
191 logging.info("PacketInExact test, port %d", of_port)
192 self.dataplane.send(of_port, pkt)
193 verify_packet_in(self, pkt, of_port, ofp.OFPR_ACTION)
194
Rich Lane692b9c02013-07-15 15:38:50 -0700195class PacketInWildcard(base_tests.SimpleDataPlane):
196 """
197 Test packet in function for a match-all flow
198
199 Send a packet to each dataplane port and verify that a packet
200 in message is received from the controller for each
201 """
202 def runTest(self):
203 delete_all_flows(self.controller)
204
205 pkt = str(simple_tcp_packet())
206
207 request = ofp.message.flow_add(
208 table_id=0,
209 cookie=42,
210 instructions=[
211 ofp.instruction.apply_actions(
212 actions=[
213 ofp.action.output(
214 port=ofp.OFPP_CONTROLLER,
215 max_len=ofp.OFPCML_NO_BUFFER)])],
216 buffer_id=ofp.OFP_NO_BUFFER,
217 priority=1000)
218
219 logging.info("Inserting flow sending all packets to controller")
220 self.controller.message_send(request)
221 do_barrier(self.controller)
222
223 for of_port in config["port_map"].keys():
224 logging.info("PacketInWildcard test, port %d", of_port)
225 self.dataplane.send(of_port, pkt)
226 verify_packet_in(self, pkt, of_port, ofp.OFPR_ACTION)
227
Rich Lane1dee22c2013-06-13 15:50:28 -0700228class PacketInMiss(base_tests.SimpleDataPlane):
229 """
230 Test packet in function for a table-miss flow
231
232 Send a packet to each dataplane port and verify that a packet
233 in message is received from the controller for each
234 """
235 def runTest(self):
236 delete_all_flows(self.controller)
237
238 parsed_pkt = simple_tcp_packet()
239 pkt = str(parsed_pkt)
240
241 request = ofp.message.flow_add(
242 table_id=0,
243 cookie=42,
244 instructions=[
245 ofp.instruction.apply_actions(
246 actions=[
247 ofp.action.output(
248 port=ofp.OFPP_CONTROLLER,
249 max_len=ofp.OFPCML_NO_BUFFER)])],
Rich Lane682db962013-07-11 20:19:27 -0700250 buffer_id=ofp.OFP_NO_BUFFER,
Rich Lane1dee22c2013-06-13 15:50:28 -0700251 priority=0)
252
253 logging.info("Inserting table-miss flow sending all packets to controller")
254 self.controller.message_send(request)
255 do_barrier(self.controller)
256
257 for of_port in config["port_map"].keys():
258 logging.info("PacketInMiss test, port %d", of_port)
259 self.dataplane.send(of_port, pkt)
260 verify_packet_in(self, pkt, of_port, ofp.OFPR_NO_MATCH)
261
262class PacketOut(base_tests.SimpleDataPlane):
263 """
264 Test packet out function
265
266 Send packet out message to controller for each dataplane port and
267 verify the packet appears on the appropriate dataplane port
268 """
269 def runTest(self):
270 pkt = str(simple_tcp_packet())
271
272 for of_port in config["port_map"].keys():
273 msg = ofp.message.packet_out(
274 in_port=ofp.OFPP_CONTROLLER,
275 actions=[ofp.action.output(port=of_port)],
276 buffer_id=ofp.OFP_NO_BUFFER,
277 data=pkt)
278
279 logging.info("PacketOut test, port %d", of_port)
280 self.controller.message_send(msg)
281 receive_pkt_verify(self, [of_port], pkt, ofp.OFPP_CONTROLLER)
282
283class FlowRemoveAll(base_tests.SimpleProtocol):
284 """
285 Remove all flows; required for almost all tests
286
287 Add a bunch of flows, remove them, and then make sure there are no flows left
288 This is an intentionally naive test to see if the baseline functionality works
289 and should be a precondition to any more complicated deletion test (e.g.,
290 delete_strict vs. delete)
291 """
292 def runTest(self):
293 for i in range(1,5):
294 logging.debug("Adding flow %d", i)
295 request = ofp.message.flow_add(
296 buffer_id=ofp.OFP_NO_BUFFER,
297 priority=i*1000)
298 self.controller.message_send(request)
299 do_barrier(self.controller)
300
301 delete_all_flows(self.controller)
302
303 logging.info("Sending flow stats request")
304 stats = get_flow_stats(self, ofp.match())
305 self.assertEqual(len(stats), 0, "Expected empty flow stats reply")
306
307
308## Multipart messages
309
310class DescStats(base_tests.SimpleProtocol):
311 """
312 Switch description multipart transaction
313
314 Only verifies we get a single reply.
315 """
316 def runTest(self):
317 request = ofp.message.desc_stats_request()
318 logging.info("Sending desc stats request")
319 response, _ = self.controller.transact(request)
320 self.assertTrue(response != None, "No response to desc stats request")
321 logging.info(response.show())
322 self.assertEquals(response.flags, 0, "Unexpected bit set in desc stats reply flags")
323
324class FlowStats(base_tests.SimpleProtocol):
325 """
326 Flow stats multipart transaction
327
328 Only verifies we get a reply.
329 """
330 def runTest(self):
331 logging.info("Sending flow stats request")
332 stats = get_flow_stats(self, ofp.match())
333 logging.info("Received %d flow stats entries", len(stats))
334 for entry in stats:
335 logging.info(entry.show())
336
337class AggregateStats(base_tests.SimpleProtocol):
338 """
339 Aggregate flow stats multipart transaction
340
341 Only verifies we get a single reply.
342 """
343 def runTest(self):
344 request = ofp.message.aggregate_stats_request(
345 table_id=ofp.OFPTT_ALL,
346 out_port=ofp.OFPP_ANY,
347 out_group=ofp.OFPG_ANY,
348 cookie=0,
349 cookie_mask=0)
350 logging.info("Sending aggregate flow stats request")
351 response, _ = self.controller.transact(request)
352 self.assertTrue(response != None, "No response to aggregate stats request")
353 logging.info(response.show())
354 self.assertEquals(response.flags, 0, "Unexpected bit set in aggregate stats reply flags")
355
356class TableStats(base_tests.SimpleProtocol):
357 """
358 Table stats multipart transaction
359
360 Only verifies we get a reply.
361 """
362 def runTest(self):
363 logging.info("Sending table stats request")
364 stats = get_stats(self, ofp.message.table_stats_request())
365 logging.info("Received %d table stats entries", len(stats))
366 for entry in stats:
367 logging.info(entry.show())
368
369class PortStats(base_tests.SimpleProtocol):
370 """
371 Port stats multipart transaction
372
373 Only verifies we get a reply.
374 """
375 def runTest(self):
376 request = ofp.message.port_stats_request(port_no=ofp.OFPP_ANY)
377 logging.info("Sending port stats request")
378 stats = get_stats(self, request)
379 logging.info("Received %d port stats entries", len(stats))
380 for entry in stats:
381 logging.info(entry.show())
382
383class QueueStats(base_tests.SimpleProtocol):
384 """
385 Queue stats multipart transaction
386
387 Only verifies we get a reply.
388 """
389 def runTest(self):
390 request = ofp.message.queue_stats_request(port_no=ofp.OFPP_ANY,
391 queue_id=ofp.OFPQ_ALL)
392 logging.info("Sending queue stats request")
393 stats = get_stats(self, request)
394 logging.info("Received %d queue stats entries", len(stats))
395 for entry in stats:
396 logging.info(entry.show())
397
398class GroupStats(base_tests.SimpleProtocol):
399 """
400 Group stats multipart transaction
401
402 Only verifies we get a reply.
403 """
404 def runTest(self):
405 request = ofp.message.group_stats_request(group_id=ofp.OFPG_ALL)
406 logging.info("Sending group stats request")
407 stats = get_stats(self, request)
408 logging.info("Received %d group stats entries", len(stats))
409 for entry in stats:
410 logging.info(entry.show())
411
412class GroupDescStats(base_tests.SimpleProtocol):
413 """
414 Group description multipart transaction
415
416 Only verifies we get a reply.
417 """
418 def runTest(self):
419 request = ofp.message.group_desc_stats_request()
420 logging.info("Sending group desc stats request")
421 stats = get_stats(self, request)
422 logging.info("Received %d group desc stats entries", len(stats))
423 for entry in stats:
424 logging.info(entry.show())
425
426class GroupFeaturesStats(base_tests.SimpleProtocol):
427 """
428 Group features multipart transaction
429
430 Only verifies we get a single reply.
431 """
432 def runTest(self):
433 request = ofp.message.group_features_stats_request()
434 logging.info("Sending group features stats request")
435 response, _ = self.controller.transact(request)
436 self.assertTrue(response != None, "No response to group features stats request")
437 logging.info(response.show())
438 self.assertEquals(response.flags, 0, "Unexpected bit set in group features stats reply flags")
439
440class MeterStats(base_tests.SimpleProtocol):
441 """
442 Meter stats multipart transaction
443
444 Only verifies we get a reply.
445 """
446 def runTest(self):
447 request = ofp.message.meter_stats_request(meter_id=ofp.OFPM_ALL)
448 logging.info("Sending meter stats request")
449 stats = get_stats(self, request)
450 logging.info("Received %d meter stats entries", len(stats))
451 for entry in stats:
452 logging.info(entry.show())
453
454class MeterConfigStats(base_tests.SimpleProtocol):
455 """
456 Meter config multipart transaction
457
458 Only verifies we get a reply.
459 """
460 def runTest(self):
461 request = ofp.message.meter_config_stats_request(meter_id=ofp.OFPM_ALL)
462 logging.info("Sending meter config stats request")
463 stats = get_stats(self, request)
464 logging.info("Received %d meter config stats entries", len(stats))
465 for entry in stats:
466 logging.info(entry.show())
467
468class MeterFeaturesStats(base_tests.SimpleProtocol):
469 """
470 Meter features multipart transaction
471
472 Only verifies we get a single reply.
473 """
474 def runTest(self):
475 request = ofp.message.meter_features_stats_request()
476 logging.info("Sending meter features stats request")
477 response, _ = self.controller.transact(request)
478 self.assertTrue(response != None, "No response to meter features stats request")
479 logging.info(response.show())
480 self.assertEquals(response.flags, 0, "Unexpected bit set in meter features stats reply flags")
481
482@disabled # pyloxi does not yet support table features
483class TableFeaturesStats(base_tests.SimpleProtocol):
484 """
485 Table features multipart transaction
486
487 Only verifies we get a reply.
488 """
489 def runTest(self):
490 logging.info("Sending table features stats request")
491 stats = get_stats(self, ofp.message.table_features_stats_request())
492 logging.info("Received %d table features stats entries", len(stats))
493 for entry in stats:
494 logging.info(entry.show())
495
496class PortDescStats(base_tests.SimpleProtocol):
497 """
498 Port description multipart transaction
499
500 Only verifies we get a reply.
501 """
502 def runTest(self):
503 logging.info("Sending port desc stats request")
504 stats = get_stats(self, ofp.message.port_desc_stats_request())
505 logging.info("Received %d port desc stats entries", len(stats))
506 for entry in stats:
507 logging.info(entry.show())
508
509class PortConfigMod(base_tests.SimpleProtocol):
510 """
511 Modify a bit in port config and verify changed
512
513 Get the switch configuration, modify the port configuration
514 and write it back; get the config again and verify changed.
515 Then set it back to the way it was.
516 """
517
518 def runTest(self):
519 logging.info("Running " + str(self))
520 for of_port, _ in config["port_map"].items(): # Grab first port
521 break
522
523 (_, config1, _) = \
524 port_config_get(self.controller, of_port)
525 self.assertTrue(config is not None, "Did not get port config")
526
Rich Laneb80c1302013-07-11 19:32:24 -0700527 logging.debug("OFPPC_NO_PACKET_IN bit port " + str(of_port) + " is now " +
Rich Lane1dee22c2013-06-13 15:50:28 -0700528 str(config1 & ofp.OFPPC_NO_PACKET_IN))
529
530 rv = port_config_set(self.controller, of_port,
531 config1 ^ ofp.OFPPC_NO_PACKET_IN,
532 ofp.OFPPC_NO_PACKET_IN)
533 self.assertTrue(rv != -1, "Error sending port mod")
534
535 # Verify change took place with same feature request
536 (_, config2, _) = port_config_get(self.controller, of_port)
537 self.assertTrue(config2 is not None, "Did not get port config2")
Rich Laneb80c1302013-07-11 19:32:24 -0700538 logging.debug("OFPPC_NO_PACKET_IN bit port " + str(of_port) + " is now " +
Rich Lane1dee22c2013-06-13 15:50:28 -0700539 str(config2 & ofp.OFPPC_NO_PACKET_IN))
540 self.assertTrue(config2 & ofp.OFPPC_NO_PACKET_IN !=
541 config1 & ofp.OFPPC_NO_PACKET_IN,
542 "Bit change did not take")
543 # Set it back
544 rv = port_config_set(self.controller, of_port, config1,
545 ofp.OFPPC_NO_PACKET_IN)
546 self.assertTrue(rv != -1, "Error sending port mod")
547
548class AsyncConfigGet(base_tests.SimpleProtocol):
549 """
550 Verify initial async config
551
552 Other tests rely on connections starting with these values.
553 """
554
555 def runTest(self):
556 logging.info("Sending get async config request")
557 response, _ = self.controller.transact(ofp.message.async_get_request())
558 self.assertTrue(response != None, "No response to get async config request")
559 logging.info(response.show())
560 self.assertEquals(response.packet_in_mask_equal_master & 0x07, 0x07)
561 self.assertEquals(response.port_status_mask_equal_master & 0x07, 0x07)
562 self.assertEquals(response.flow_removed_mask_equal_master & 0x0f, 0x0f)