blob: 0e3aed872ba0c1cda70e78bd58ef13ef362fe8e3 [file] [log] [blame]
Matteo Scandoloa229eca2017-08-08 13:05:28 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
Rich Lane1dee22c2013-06-13 15:50:28 -070017# Distributed under the OpenFlow Software License (see LICENSE)
18# Copyright (c) 2010 The Board of Trustees of The Leland Stanford Junior University
19# Copyright (c) 2012, 2013 Big Switch Networks, Inc.
20# Copyright (c) 2012, 2013 CPqD
21# Copyright (c) 2012, 2013 Ericsson
Rich Lane1ab6f832013-05-03 17:51:35 -070022"""
23Basic test cases
24
25Test cases in other modules depend on this functionality.
26"""
27
Rich Lane1dee22c2013-06-13 15:50:28 -070028import logging
29
Rich Lane1ab6f832013-05-03 17:51:35 -070030from oftest import config
31import oftest.base_tests as base_tests
32import ofp
33
34from oftest.testutils import *
35
36@group('smoke')
37class Echo(base_tests.SimpleProtocol):
38 """
39 Test echo response with no data
40 """
41 def runTest(self):
42 request = ofp.message.echo_request()
43 response, pkt = 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(len(response.data), 0, 'response data non-empty')
Rich Lane1dee22c2013-06-13 15:50:28 -070051
52class EchoWithData(base_tests.SimpleProtocol):
53 """
54 Test echo response with short string data
55 """
56 def runTest(self):
57 data = 'OpenFlow Will Rule The World'
58 request = ofp.message.echo_request(data=data)
59 response, _ = self.controller.transact(request)
60 self.assertTrue(response is not None,
61 "Did not get echo reply")
62 self.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
63 'response is not echo_reply')
64 self.assertEqual(request.xid, response.xid,
65 'response xid != request xid')
66 self.assertEqual(request.data, response.data,
67 'response data != request data')
68
69class FeaturesRequest(base_tests.SimpleProtocol):
70 """
71 Test features_request to make sure we get a response
72
73 Does NOT test the contents; just that we get a response
74 """
75 def runTest(self):
76 request = ofp.message.features_request()
77 response,_ = self.controller.transact(request)
78 self.assertTrue(response is not None,
79 'Did not get features reply')
80
Rich Lane82c882d2013-08-09 17:13:52 -070081class DefaultDrop(base_tests.SimpleDataPlane):
82 """
83 Check that an empty flowtable results in drops
84 """
85 def runTest(self):
86 in_port, = openflow_ports(1)
87 delete_all_flows(self.controller)
88
89 pkt = str(simple_tcp_packet())
90 self.dataplane.send(in_port, pkt)
91 verify_no_packet_in(self, pkt, None)
Rich Lanee4b384d2013-09-13 14:33:40 -070092 verify_packets(self, pkt, [])
Rich Lane82c882d2013-08-09 17:13:52 -070093
Rich Lane1dee22c2013-06-13 15:50:28 -070094class OutputExact(base_tests.SimpleDataPlane):
95 """
96 Test output function for an exact-match flow
97
98 For each port A, adds a flow directing matching packets to that port.
Rich Laneb80c1302013-07-11 19:32:24 -070099 Then, for all other ports B != A, verifies that sending a matching packet
Rich Lane1dee22c2013-06-13 15:50:28 -0700100 to B results in an output to A.
101 """
102 def runTest(self):
103 ports = sorted(config["port_map"].keys())
104
105 delete_all_flows(self.controller)
106
107 parsed_pkt = simple_tcp_packet()
108 pkt = str(parsed_pkt)
109 match = packet_to_flow_match(self, parsed_pkt)
110
111 for out_port in ports:
112 request = ofp.message.flow_add(
Wilson Ng6f539642013-10-28 18:17:44 -0700113 table_id=test_param_get("table", 0),
Rich Lane1dee22c2013-06-13 15:50:28 -0700114 cookie=42,
115 match=match,
116 instructions=[
117 ofp.instruction.apply_actions(
118 actions=[
119 ofp.action.output(
120 port=out_port,
121 max_len=ofp.OFPCML_NO_BUFFER)])],
Rich Laneb80c1302013-07-11 19:32:24 -0700122 buffer_id=ofp.OFP_NO_BUFFER,
Rich Lane1dee22c2013-06-13 15:50:28 -0700123 priority=1000)
124
125 logging.info("Inserting flow sending matching packets to port %d", out_port)
126 self.controller.message_send(request)
127 do_barrier(self.controller)
128
129 for in_port in ports:
130 if in_port == out_port:
131 continue
132 logging.info("OutputExact test, ports %d to %d", in_port, out_port)
133 self.dataplane.send(in_port, pkt)
Rich Lanee4b384d2013-09-13 14:33:40 -0700134 verify_packets(self, pkt, [out_port])
Rich Lane1dee22c2013-06-13 15:50:28 -0700135
Rich Lane692b9c02013-07-15 15:38:50 -0700136class OutputWildcard(base_tests.SimpleDataPlane):
137 """
138 Test output function for a match-all (but not table-miss) flow
139
140 For each port A, adds a flow directing all packets to that port.
141 Then, for all other ports B != A, verifies that sending a packet
142 to B results in an output to A.
143 """
144 def runTest(self):
145 ports = sorted(config["port_map"].keys())
146
147 delete_all_flows(self.controller)
148
149 pkt = str(simple_tcp_packet())
150
151 for out_port in ports:
152 request = ofp.message.flow_add(
Wilson Ng6f539642013-10-28 18:17:44 -0700153 table_id=test_param_get("table", 0),
Rich Lane692b9c02013-07-15 15:38:50 -0700154 cookie=42,
155 instructions=[
156 ofp.instruction.apply_actions(
157 actions=[
158 ofp.action.output(
159 port=out_port,
160 max_len=ofp.OFPCML_NO_BUFFER)])],
161 buffer_id=ofp.OFP_NO_BUFFER,
162 priority=1000)
163
164 logging.info("Inserting flow sending all packets to port %d", out_port)
165 self.controller.message_send(request)
166 do_barrier(self.controller)
167
168 for in_port in ports:
169 if in_port == out_port:
170 continue
171 logging.info("OutputWildcard test, ports %d to %d", in_port, out_port)
172 self.dataplane.send(in_port, pkt)
Rich Lanee4b384d2013-09-13 14:33:40 -0700173 verify_packets(self, pkt, [out_port])
Rich Lane692b9c02013-07-15 15:38:50 -0700174
Rich Lane1dee22c2013-06-13 15:50:28 -0700175class PacketInExact(base_tests.SimpleDataPlane):
176 """
177 Test packet in function for an exact-match flow
178
179 Send a packet to each dataplane port and verify that a packet
180 in message is received from the controller for each
181 """
182 def runTest(self):
183 delete_all_flows(self.controller)
184
185 parsed_pkt = simple_tcp_packet()
186 pkt = str(parsed_pkt)
187 match = packet_to_flow_match(self, parsed_pkt)
188
189 request = ofp.message.flow_add(
Wilson Ng6f539642013-10-28 18:17:44 -0700190 table_id=test_param_get("table", 0),
Rich Lane1dee22c2013-06-13 15:50:28 -0700191 cookie=42,
192 match=match,
193 instructions=[
194 ofp.instruction.apply_actions(
195 actions=[
196 ofp.action.output(
197 port=ofp.OFPP_CONTROLLER,
198 max_len=ofp.OFPCML_NO_BUFFER)])],
Rich Lane682db962013-07-11 20:19:27 -0700199 buffer_id=ofp.OFP_NO_BUFFER,
Rich Lane1dee22c2013-06-13 15:50:28 -0700200 priority=1000)
201
202 logging.info("Inserting flow sending matching packets to controller")
203 self.controller.message_send(request)
204 do_barrier(self.controller)
205
206 for of_port in config["port_map"].keys():
207 logging.info("PacketInExact test, port %d", of_port)
208 self.dataplane.send(of_port, pkt)
209 verify_packet_in(self, pkt, of_port, ofp.OFPR_ACTION)
Rich Lanee4b384d2013-09-13 14:33:40 -0700210 verify_packets(self, pkt, [])
Rich Lane1dee22c2013-06-13 15:50:28 -0700211
Rich Lane692b9c02013-07-15 15:38:50 -0700212class PacketInWildcard(base_tests.SimpleDataPlane):
213 """
214 Test packet in function for a match-all flow
215
216 Send a packet to each dataplane port and verify that a packet
217 in message is received from the controller for each
218 """
219 def runTest(self):
220 delete_all_flows(self.controller)
221
222 pkt = str(simple_tcp_packet())
223
224 request = ofp.message.flow_add(
Wilson Ng6f539642013-10-28 18:17:44 -0700225 table_id=test_param_get("table", 0),
Rich Lane692b9c02013-07-15 15:38:50 -0700226 cookie=42,
227 instructions=[
228 ofp.instruction.apply_actions(
229 actions=[
230 ofp.action.output(
231 port=ofp.OFPP_CONTROLLER,
232 max_len=ofp.OFPCML_NO_BUFFER)])],
233 buffer_id=ofp.OFP_NO_BUFFER,
234 priority=1000)
235
236 logging.info("Inserting flow sending all packets to controller")
237 self.controller.message_send(request)
238 do_barrier(self.controller)
239
240 for of_port in config["port_map"].keys():
241 logging.info("PacketInWildcard test, port %d", of_port)
242 self.dataplane.send(of_port, pkt)
243 verify_packet_in(self, pkt, of_port, ofp.OFPR_ACTION)
Rich Lanee4b384d2013-09-13 14:33:40 -0700244 verify_packets(self, pkt, [])
Rich Lane692b9c02013-07-15 15:38:50 -0700245
Rich Lane1dee22c2013-06-13 15:50:28 -0700246class PacketInMiss(base_tests.SimpleDataPlane):
247 """
248 Test packet in function for a table-miss flow
249
250 Send a packet to each dataplane port and verify that a packet
251 in message is received from the controller for each
252 """
253 def runTest(self):
254 delete_all_flows(self.controller)
255
256 parsed_pkt = simple_tcp_packet()
257 pkt = str(parsed_pkt)
258
259 request = ofp.message.flow_add(
Wilson Ng6f539642013-10-28 18:17:44 -0700260 table_id=test_param_get("table", 0),
Rich Lane1dee22c2013-06-13 15:50:28 -0700261 cookie=42,
262 instructions=[
263 ofp.instruction.apply_actions(
264 actions=[
265 ofp.action.output(
266 port=ofp.OFPP_CONTROLLER,
267 max_len=ofp.OFPCML_NO_BUFFER)])],
Rich Lane682db962013-07-11 20:19:27 -0700268 buffer_id=ofp.OFP_NO_BUFFER,
Rich Lane1dee22c2013-06-13 15:50:28 -0700269 priority=0)
270
271 logging.info("Inserting table-miss flow sending all packets to controller")
272 self.controller.message_send(request)
273 do_barrier(self.controller)
274
275 for of_port in config["port_map"].keys():
276 logging.info("PacketInMiss test, port %d", of_port)
277 self.dataplane.send(of_port, pkt)
278 verify_packet_in(self, pkt, of_port, ofp.OFPR_NO_MATCH)
Rich Lanee4b384d2013-09-13 14:33:40 -0700279 verify_packets(self, pkt, [])
Rich Lane1dee22c2013-06-13 15:50:28 -0700280
281class PacketOut(base_tests.SimpleDataPlane):
282 """
283 Test packet out function
284
285 Send packet out message to controller for each dataplane port and
286 verify the packet appears on the appropriate dataplane port
287 """
288 def runTest(self):
289 pkt = str(simple_tcp_packet())
290
291 for of_port in config["port_map"].keys():
292 msg = ofp.message.packet_out(
293 in_port=ofp.OFPP_CONTROLLER,
294 actions=[ofp.action.output(port=of_port)],
295 buffer_id=ofp.OFP_NO_BUFFER,
296 data=pkt)
297
298 logging.info("PacketOut test, port %d", of_port)
299 self.controller.message_send(msg)
Rich Lanee4b384d2013-09-13 14:33:40 -0700300 verify_packets(self, pkt, [of_port])
Rich Lane1dee22c2013-06-13 15:50:28 -0700301
302class FlowRemoveAll(base_tests.SimpleProtocol):
303 """
304 Remove all flows; required for almost all tests
305
306 Add a bunch of flows, remove them, and then make sure there are no flows left
307 This is an intentionally naive test to see if the baseline functionality works
308 and should be a precondition to any more complicated deletion test (e.g.,
309 delete_strict vs. delete)
310 """
311 def runTest(self):
312 for i in range(1,5):
313 logging.debug("Adding flow %d", i)
314 request = ofp.message.flow_add(
315 buffer_id=ofp.OFP_NO_BUFFER,
316 priority=i*1000)
317 self.controller.message_send(request)
318 do_barrier(self.controller)
319
320 delete_all_flows(self.controller)
321
322 logging.info("Sending flow stats request")
323 stats = get_flow_stats(self, ofp.match())
324 self.assertEqual(len(stats), 0, "Expected empty flow stats reply")
325
326
327## Multipart messages
328
329class DescStats(base_tests.SimpleProtocol):
330 """
331 Switch description multipart transaction
332
333 Only verifies we get a single reply.
334 """
335 def runTest(self):
336 request = ofp.message.desc_stats_request()
337 logging.info("Sending desc stats request")
338 response, _ = self.controller.transact(request)
339 self.assertTrue(response != None, "No response to desc stats request")
340 logging.info(response.show())
341 self.assertEquals(response.flags, 0, "Unexpected bit set in desc stats reply flags")
342
343class FlowStats(base_tests.SimpleProtocol):
344 """
345 Flow stats multipart transaction
346
347 Only verifies we get a reply.
348 """
349 def runTest(self):
350 logging.info("Sending flow stats request")
351 stats = get_flow_stats(self, ofp.match())
352 logging.info("Received %d flow stats entries", len(stats))
353 for entry in stats:
354 logging.info(entry.show())
355
356class AggregateStats(base_tests.SimpleProtocol):
357 """
358 Aggregate flow stats multipart transaction
359
360 Only verifies we get a single reply.
361 """
362 def runTest(self):
363 request = ofp.message.aggregate_stats_request(
364 table_id=ofp.OFPTT_ALL,
365 out_port=ofp.OFPP_ANY,
366 out_group=ofp.OFPG_ANY,
367 cookie=0,
368 cookie_mask=0)
369 logging.info("Sending aggregate flow stats request")
370 response, _ = self.controller.transact(request)
371 self.assertTrue(response != None, "No response to aggregate stats request")
372 logging.info(response.show())
373 self.assertEquals(response.flags, 0, "Unexpected bit set in aggregate stats reply flags")
374
375class TableStats(base_tests.SimpleProtocol):
376 """
377 Table stats multipart transaction
378
379 Only verifies we get a reply.
380 """
381 def runTest(self):
382 logging.info("Sending table stats request")
383 stats = get_stats(self, ofp.message.table_stats_request())
384 logging.info("Received %d table stats entries", len(stats))
385 for entry in stats:
386 logging.info(entry.show())
387
388class PortStats(base_tests.SimpleProtocol):
389 """
390 Port stats multipart transaction
391
392 Only verifies we get a reply.
393 """
394 def runTest(self):
395 request = ofp.message.port_stats_request(port_no=ofp.OFPP_ANY)
396 logging.info("Sending port stats request")
397 stats = get_stats(self, request)
398 logging.info("Received %d port stats entries", len(stats))
399 for entry in stats:
400 logging.info(entry.show())
401
402class QueueStats(base_tests.SimpleProtocol):
403 """
404 Queue stats multipart transaction
405
406 Only verifies we get a reply.
407 """
408 def runTest(self):
409 request = ofp.message.queue_stats_request(port_no=ofp.OFPP_ANY,
410 queue_id=ofp.OFPQ_ALL)
411 logging.info("Sending queue stats request")
412 stats = get_stats(self, request)
413 logging.info("Received %d queue stats entries", len(stats))
414 for entry in stats:
415 logging.info(entry.show())
416
417class GroupStats(base_tests.SimpleProtocol):
418 """
419 Group stats multipart transaction
420
421 Only verifies we get a reply.
422 """
423 def runTest(self):
424 request = ofp.message.group_stats_request(group_id=ofp.OFPG_ALL)
425 logging.info("Sending group stats request")
426 stats = get_stats(self, request)
427 logging.info("Received %d group stats entries", len(stats))
428 for entry in stats:
429 logging.info(entry.show())
430
431class GroupDescStats(base_tests.SimpleProtocol):
432 """
433 Group description multipart transaction
434
435 Only verifies we get a reply.
436 """
437 def runTest(self):
438 request = ofp.message.group_desc_stats_request()
439 logging.info("Sending group desc stats request")
440 stats = get_stats(self, request)
441 logging.info("Received %d group desc stats entries", len(stats))
442 for entry in stats:
443 logging.info(entry.show())
444
445class GroupFeaturesStats(base_tests.SimpleProtocol):
446 """
447 Group features multipart transaction
448
449 Only verifies we get a single reply.
450 """
451 def runTest(self):
452 request = ofp.message.group_features_stats_request()
453 logging.info("Sending group features stats request")
454 response, _ = self.controller.transact(request)
455 self.assertTrue(response != None, "No response to group features stats request")
456 logging.info(response.show())
457 self.assertEquals(response.flags, 0, "Unexpected bit set in group features stats reply flags")
458
459class MeterStats(base_tests.SimpleProtocol):
460 """
461 Meter stats multipart transaction
462
463 Only verifies we get a reply.
464 """
465 def runTest(self):
466 request = ofp.message.meter_stats_request(meter_id=ofp.OFPM_ALL)
467 logging.info("Sending meter stats request")
468 stats = get_stats(self, request)
469 logging.info("Received %d meter stats entries", len(stats))
470 for entry in stats:
471 logging.info(entry.show())
472
473class MeterConfigStats(base_tests.SimpleProtocol):
474 """
475 Meter config multipart transaction
476
477 Only verifies we get a reply.
478 """
479 def runTest(self):
480 request = ofp.message.meter_config_stats_request(meter_id=ofp.OFPM_ALL)
481 logging.info("Sending meter config stats request")
482 stats = get_stats(self, request)
483 logging.info("Received %d meter config stats entries", len(stats))
484 for entry in stats:
485 logging.info(entry.show())
486
487class MeterFeaturesStats(base_tests.SimpleProtocol):
488 """
489 Meter features multipart transaction
490
491 Only verifies we get a single reply.
492 """
493 def runTest(self):
494 request = ofp.message.meter_features_stats_request()
495 logging.info("Sending meter features stats request")
496 response, _ = self.controller.transact(request)
497 self.assertTrue(response != None, "No response to meter features stats request")
498 logging.info(response.show())
499 self.assertEquals(response.flags, 0, "Unexpected bit set in meter features stats reply flags")
500
501@disabled # pyloxi does not yet support table features
502class TableFeaturesStats(base_tests.SimpleProtocol):
503 """
504 Table features multipart transaction
505
506 Only verifies we get a reply.
507 """
508 def runTest(self):
509 logging.info("Sending table features stats request")
510 stats = get_stats(self, ofp.message.table_features_stats_request())
511 logging.info("Received %d table features stats entries", len(stats))
512 for entry in stats:
513 logging.info(entry.show())
514
515class PortDescStats(base_tests.SimpleProtocol):
516 """
517 Port description multipart transaction
518
519 Only verifies we get a reply.
520 """
521 def runTest(self):
522 logging.info("Sending port desc stats request")
523 stats = get_stats(self, ofp.message.port_desc_stats_request())
524 logging.info("Received %d port desc stats entries", len(stats))
525 for entry in stats:
526 logging.info(entry.show())
527
528class PortConfigMod(base_tests.SimpleProtocol):
529 """
530 Modify a bit in port config and verify changed
531
532 Get the switch configuration, modify the port configuration
533 and write it back; get the config again and verify changed.
534 Then set it back to the way it was.
535 """
536
537 def runTest(self):
538 logging.info("Running " + str(self))
539 for of_port, _ in config["port_map"].items(): # Grab first port
540 break
541
542 (_, config1, _) = \
543 port_config_get(self.controller, of_port)
544 self.assertTrue(config is not None, "Did not get port config")
545
Rich Laneb80c1302013-07-11 19:32:24 -0700546 logging.debug("OFPPC_NO_PACKET_IN bit port " + str(of_port) + " is now " +
Rich Lane1dee22c2013-06-13 15:50:28 -0700547 str(config1 & ofp.OFPPC_NO_PACKET_IN))
548
549 rv = port_config_set(self.controller, of_port,
550 config1 ^ ofp.OFPPC_NO_PACKET_IN,
551 ofp.OFPPC_NO_PACKET_IN)
552 self.assertTrue(rv != -1, "Error sending port mod")
553
554 # Verify change took place with same feature request
555 (_, config2, _) = port_config_get(self.controller, of_port)
556 self.assertTrue(config2 is not None, "Did not get port config2")
Rich Laneb80c1302013-07-11 19:32:24 -0700557 logging.debug("OFPPC_NO_PACKET_IN bit port " + str(of_port) + " is now " +
Rich Lane1dee22c2013-06-13 15:50:28 -0700558 str(config2 & ofp.OFPPC_NO_PACKET_IN))
559 self.assertTrue(config2 & ofp.OFPPC_NO_PACKET_IN !=
560 config1 & ofp.OFPPC_NO_PACKET_IN,
561 "Bit change did not take")
562 # Set it back
563 rv = port_config_set(self.controller, of_port, config1,
564 ofp.OFPPC_NO_PACKET_IN)
565 self.assertTrue(rv != -1, "Error sending port mod")
566
567class AsyncConfigGet(base_tests.SimpleProtocol):
568 """
569 Verify initial async config
570
571 Other tests rely on connections starting with these values.
572 """
573
574 def runTest(self):
575 logging.info("Sending get async config request")
576 response, _ = self.controller.transact(ofp.message.async_get_request())
577 self.assertTrue(response != None, "No response to get async config request")
578 logging.info(response.show())
579 self.assertEquals(response.packet_in_mask_equal_master & 0x07, 0x07)
580 self.assertEquals(response.port_status_mask_equal_master & 0x07, 0x07)
581 self.assertEquals(response.flow_removed_mask_equal_master & 0x0f, 0x0f)