blob: 21dd130cdf9ba7c9634af1906397eaacaf214b2b [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)
Rich Lanee4b384d2013-09-13 14:33:40 -070076 verify_packets(self, pkt, [])
Rich Lane82c882d2013-08-09 17:13:52 -070077
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)
Rich Lanee4b384d2013-09-13 14:33:40 -0700118 verify_packets(self, pkt, [out_port])
Rich Lane1dee22c2013-06-13 15:50:28 -0700119
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)
Rich Lanee4b384d2013-09-13 14:33:40 -0700157 verify_packets(self, pkt, [out_port])
Rich Lane692b9c02013-07-15 15:38:50 -0700158
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)
Rich Lanee4b384d2013-09-13 14:33:40 -0700194 verify_packets(self, pkt, [])
Rich Lane1dee22c2013-06-13 15:50:28 -0700195
Rich Lane692b9c02013-07-15 15:38:50 -0700196class PacketInWildcard(base_tests.SimpleDataPlane):
197 """
198 Test packet in function for a match-all flow
199
200 Send a packet to each dataplane port and verify that a packet
201 in message is received from the controller for each
202 """
203 def runTest(self):
204 delete_all_flows(self.controller)
205
206 pkt = str(simple_tcp_packet())
207
208 request = ofp.message.flow_add(
209 table_id=0,
210 cookie=42,
211 instructions=[
212 ofp.instruction.apply_actions(
213 actions=[
214 ofp.action.output(
215 port=ofp.OFPP_CONTROLLER,
216 max_len=ofp.OFPCML_NO_BUFFER)])],
217 buffer_id=ofp.OFP_NO_BUFFER,
218 priority=1000)
219
220 logging.info("Inserting flow sending all packets to controller")
221 self.controller.message_send(request)
222 do_barrier(self.controller)
223
224 for of_port in config["port_map"].keys():
225 logging.info("PacketInWildcard test, port %d", of_port)
226 self.dataplane.send(of_port, pkt)
227 verify_packet_in(self, pkt, of_port, ofp.OFPR_ACTION)
Rich Lanee4b384d2013-09-13 14:33:40 -0700228 verify_packets(self, pkt, [])
Rich Lane692b9c02013-07-15 15:38:50 -0700229
Rich Lane1dee22c2013-06-13 15:50:28 -0700230class PacketInMiss(base_tests.SimpleDataPlane):
231 """
232 Test packet in function for a table-miss flow
233
234 Send a packet to each dataplane port and verify that a packet
235 in message is received from the controller for each
236 """
237 def runTest(self):
238 delete_all_flows(self.controller)
239
240 parsed_pkt = simple_tcp_packet()
241 pkt = str(parsed_pkt)
242
243 request = ofp.message.flow_add(
244 table_id=0,
245 cookie=42,
246 instructions=[
247 ofp.instruction.apply_actions(
248 actions=[
249 ofp.action.output(
250 port=ofp.OFPP_CONTROLLER,
251 max_len=ofp.OFPCML_NO_BUFFER)])],
Rich Lane682db962013-07-11 20:19:27 -0700252 buffer_id=ofp.OFP_NO_BUFFER,
Rich Lane1dee22c2013-06-13 15:50:28 -0700253 priority=0)
254
255 logging.info("Inserting table-miss flow sending all packets to controller")
256 self.controller.message_send(request)
257 do_barrier(self.controller)
258
259 for of_port in config["port_map"].keys():
260 logging.info("PacketInMiss test, port %d", of_port)
261 self.dataplane.send(of_port, pkt)
262 verify_packet_in(self, pkt, of_port, ofp.OFPR_NO_MATCH)
Rich Lanee4b384d2013-09-13 14:33:40 -0700263 verify_packets(self, pkt, [])
Rich Lane1dee22c2013-06-13 15:50:28 -0700264
265class PacketOut(base_tests.SimpleDataPlane):
266 """
267 Test packet out function
268
269 Send packet out message to controller for each dataplane port and
270 verify the packet appears on the appropriate dataplane port
271 """
272 def runTest(self):
273 pkt = str(simple_tcp_packet())
274
275 for of_port in config["port_map"].keys():
276 msg = ofp.message.packet_out(
277 in_port=ofp.OFPP_CONTROLLER,
278 actions=[ofp.action.output(port=of_port)],
279 buffer_id=ofp.OFP_NO_BUFFER,
280 data=pkt)
281
282 logging.info("PacketOut test, port %d", of_port)
283 self.controller.message_send(msg)
Rich Lanee4b384d2013-09-13 14:33:40 -0700284 verify_packets(self, pkt, [of_port])
Rich Lane1dee22c2013-06-13 15:50:28 -0700285
286class FlowRemoveAll(base_tests.SimpleProtocol):
287 """
288 Remove all flows; required for almost all tests
289
290 Add a bunch of flows, remove them, and then make sure there are no flows left
291 This is an intentionally naive test to see if the baseline functionality works
292 and should be a precondition to any more complicated deletion test (e.g.,
293 delete_strict vs. delete)
294 """
295 def runTest(self):
296 for i in range(1,5):
297 logging.debug("Adding flow %d", i)
298 request = ofp.message.flow_add(
299 buffer_id=ofp.OFP_NO_BUFFER,
300 priority=i*1000)
301 self.controller.message_send(request)
302 do_barrier(self.controller)
303
304 delete_all_flows(self.controller)
305
306 logging.info("Sending flow stats request")
307 stats = get_flow_stats(self, ofp.match())
308 self.assertEqual(len(stats), 0, "Expected empty flow stats reply")
309
310
311## Multipart messages
312
313class DescStats(base_tests.SimpleProtocol):
314 """
315 Switch description multipart transaction
316
317 Only verifies we get a single reply.
318 """
319 def runTest(self):
320 request = ofp.message.desc_stats_request()
321 logging.info("Sending desc stats request")
322 response, _ = self.controller.transact(request)
323 self.assertTrue(response != None, "No response to desc stats request")
324 logging.info(response.show())
325 self.assertEquals(response.flags, 0, "Unexpected bit set in desc stats reply flags")
326
327class FlowStats(base_tests.SimpleProtocol):
328 """
329 Flow stats multipart transaction
330
331 Only verifies we get a reply.
332 """
333 def runTest(self):
334 logging.info("Sending flow stats request")
335 stats = get_flow_stats(self, ofp.match())
336 logging.info("Received %d flow stats entries", len(stats))
337 for entry in stats:
338 logging.info(entry.show())
339
340class AggregateStats(base_tests.SimpleProtocol):
341 """
342 Aggregate flow stats multipart transaction
343
344 Only verifies we get a single reply.
345 """
346 def runTest(self):
347 request = ofp.message.aggregate_stats_request(
348 table_id=ofp.OFPTT_ALL,
349 out_port=ofp.OFPP_ANY,
350 out_group=ofp.OFPG_ANY,
351 cookie=0,
352 cookie_mask=0)
353 logging.info("Sending aggregate flow stats request")
354 response, _ = self.controller.transact(request)
355 self.assertTrue(response != None, "No response to aggregate stats request")
356 logging.info(response.show())
357 self.assertEquals(response.flags, 0, "Unexpected bit set in aggregate stats reply flags")
358
359class TableStats(base_tests.SimpleProtocol):
360 """
361 Table stats multipart transaction
362
363 Only verifies we get a reply.
364 """
365 def runTest(self):
366 logging.info("Sending table stats request")
367 stats = get_stats(self, ofp.message.table_stats_request())
368 logging.info("Received %d table stats entries", len(stats))
369 for entry in stats:
370 logging.info(entry.show())
371
372class PortStats(base_tests.SimpleProtocol):
373 """
374 Port stats multipart transaction
375
376 Only verifies we get a reply.
377 """
378 def runTest(self):
379 request = ofp.message.port_stats_request(port_no=ofp.OFPP_ANY)
380 logging.info("Sending port stats request")
381 stats = get_stats(self, request)
382 logging.info("Received %d port stats entries", len(stats))
383 for entry in stats:
384 logging.info(entry.show())
385
386class QueueStats(base_tests.SimpleProtocol):
387 """
388 Queue stats multipart transaction
389
390 Only verifies we get a reply.
391 """
392 def runTest(self):
393 request = ofp.message.queue_stats_request(port_no=ofp.OFPP_ANY,
394 queue_id=ofp.OFPQ_ALL)
395 logging.info("Sending queue stats request")
396 stats = get_stats(self, request)
397 logging.info("Received %d queue stats entries", len(stats))
398 for entry in stats:
399 logging.info(entry.show())
400
401class GroupStats(base_tests.SimpleProtocol):
402 """
403 Group stats multipart transaction
404
405 Only verifies we get a reply.
406 """
407 def runTest(self):
408 request = ofp.message.group_stats_request(group_id=ofp.OFPG_ALL)
409 logging.info("Sending group stats request")
410 stats = get_stats(self, request)
411 logging.info("Received %d group stats entries", len(stats))
412 for entry in stats:
413 logging.info(entry.show())
414
415class GroupDescStats(base_tests.SimpleProtocol):
416 """
417 Group description multipart transaction
418
419 Only verifies we get a reply.
420 """
421 def runTest(self):
422 request = ofp.message.group_desc_stats_request()
423 logging.info("Sending group desc stats request")
424 stats = get_stats(self, request)
425 logging.info("Received %d group desc stats entries", len(stats))
426 for entry in stats:
427 logging.info(entry.show())
428
429class GroupFeaturesStats(base_tests.SimpleProtocol):
430 """
431 Group features multipart transaction
432
433 Only verifies we get a single reply.
434 """
435 def runTest(self):
436 request = ofp.message.group_features_stats_request()
437 logging.info("Sending group features stats request")
438 response, _ = self.controller.transact(request)
439 self.assertTrue(response != None, "No response to group features stats request")
440 logging.info(response.show())
441 self.assertEquals(response.flags, 0, "Unexpected bit set in group features stats reply flags")
442
443class MeterStats(base_tests.SimpleProtocol):
444 """
445 Meter stats multipart transaction
446
447 Only verifies we get a reply.
448 """
449 def runTest(self):
450 request = ofp.message.meter_stats_request(meter_id=ofp.OFPM_ALL)
451 logging.info("Sending meter stats request")
452 stats = get_stats(self, request)
453 logging.info("Received %d meter stats entries", len(stats))
454 for entry in stats:
455 logging.info(entry.show())
456
457class MeterConfigStats(base_tests.SimpleProtocol):
458 """
459 Meter config multipart transaction
460
461 Only verifies we get a reply.
462 """
463 def runTest(self):
464 request = ofp.message.meter_config_stats_request(meter_id=ofp.OFPM_ALL)
465 logging.info("Sending meter config stats request")
466 stats = get_stats(self, request)
467 logging.info("Received %d meter config stats entries", len(stats))
468 for entry in stats:
469 logging.info(entry.show())
470
471class MeterFeaturesStats(base_tests.SimpleProtocol):
472 """
473 Meter features multipart transaction
474
475 Only verifies we get a single reply.
476 """
477 def runTest(self):
478 request = ofp.message.meter_features_stats_request()
479 logging.info("Sending meter features stats request")
480 response, _ = self.controller.transact(request)
481 self.assertTrue(response != None, "No response to meter features stats request")
482 logging.info(response.show())
483 self.assertEquals(response.flags, 0, "Unexpected bit set in meter features stats reply flags")
484
485@disabled # pyloxi does not yet support table features
486class TableFeaturesStats(base_tests.SimpleProtocol):
487 """
488 Table features multipart transaction
489
490 Only verifies we get a reply.
491 """
492 def runTest(self):
493 logging.info("Sending table features stats request")
494 stats = get_stats(self, ofp.message.table_features_stats_request())
495 logging.info("Received %d table features stats entries", len(stats))
496 for entry in stats:
497 logging.info(entry.show())
498
499class PortDescStats(base_tests.SimpleProtocol):
500 """
501 Port description multipart transaction
502
503 Only verifies we get a reply.
504 """
505 def runTest(self):
506 logging.info("Sending port desc stats request")
507 stats = get_stats(self, ofp.message.port_desc_stats_request())
508 logging.info("Received %d port desc stats entries", len(stats))
509 for entry in stats:
510 logging.info(entry.show())
511
512class PortConfigMod(base_tests.SimpleProtocol):
513 """
514 Modify a bit in port config and verify changed
515
516 Get the switch configuration, modify the port configuration
517 and write it back; get the config again and verify changed.
518 Then set it back to the way it was.
519 """
520
521 def runTest(self):
522 logging.info("Running " + str(self))
523 for of_port, _ in config["port_map"].items(): # Grab first port
524 break
525
526 (_, config1, _) = \
527 port_config_get(self.controller, of_port)
528 self.assertTrue(config is not None, "Did not get port config")
529
Rich Laneb80c1302013-07-11 19:32:24 -0700530 logging.debug("OFPPC_NO_PACKET_IN bit port " + str(of_port) + " is now " +
Rich Lane1dee22c2013-06-13 15:50:28 -0700531 str(config1 & ofp.OFPPC_NO_PACKET_IN))
532
533 rv = port_config_set(self.controller, of_port,
534 config1 ^ ofp.OFPPC_NO_PACKET_IN,
535 ofp.OFPPC_NO_PACKET_IN)
536 self.assertTrue(rv != -1, "Error sending port mod")
537
538 # Verify change took place with same feature request
539 (_, config2, _) = port_config_get(self.controller, of_port)
540 self.assertTrue(config2 is not None, "Did not get port config2")
Rich Laneb80c1302013-07-11 19:32:24 -0700541 logging.debug("OFPPC_NO_PACKET_IN bit port " + str(of_port) + " is now " +
Rich Lane1dee22c2013-06-13 15:50:28 -0700542 str(config2 & ofp.OFPPC_NO_PACKET_IN))
543 self.assertTrue(config2 & ofp.OFPPC_NO_PACKET_IN !=
544 config1 & ofp.OFPPC_NO_PACKET_IN,
545 "Bit change did not take")
546 # Set it back
547 rv = port_config_set(self.controller, of_port, config1,
548 ofp.OFPPC_NO_PACKET_IN)
549 self.assertTrue(rv != -1, "Error sending port mod")
550
551class AsyncConfigGet(base_tests.SimpleProtocol):
552 """
553 Verify initial async config
554
555 Other tests rely on connections starting with these values.
556 """
557
558 def runTest(self):
559 logging.info("Sending get async config request")
560 response, _ = self.controller.transact(ofp.message.async_get_request())
561 self.assertTrue(response != None, "No response to get async config request")
562 logging.info(response.show())
563 self.assertEquals(response.packet_in_mask_equal_master & 0x07, 0x07)
564 self.assertEquals(response.port_status_mask_equal_master & 0x07, 0x07)
565 self.assertEquals(response.flow_removed_mask_equal_master & 0x0f, 0x0f)