blob: b2392a729b7bd3d31d12d12760786f15b1a2d39d [file] [log] [blame]
Flavio Castro96646c62015-11-16 15:05:43 -05001# 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
6"""
7Basic test cases
8
9Test cases in other modules depend on this functionality.
10"""
11
12import logging
13
14from oftest import config
15import oftest.base_tests as base_tests
16import ofp
17import ofdpa_utils
18
19from oftest.testutils import *
20
21@group('smoke')
22class Echo(base_tests.SimpleProtocol):
23 """
24 Test echo response with no data
25 """
26 def runTest(self):
27 request = ofp.message.echo_request()
28 response, pkt = self.controller.transact(request)
29 self.assertTrue(response is not None,
30 "Did not get echo reply")
31 self.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
32 'response is not echo_reply')
33 self.assertEqual(request.xid, response.xid,
34 'response xid != request xid')
35 self.assertEqual(len(response.data), 0, 'response data non-empty')
36
37class EchoWithData(base_tests.SimpleProtocol):
38 """
39 Test echo response with short string data
40 """
41 def runTest(self):
42 data = 'OpenFlow Will Rule The World'
43 request = ofp.message.echo_request(data=data)
44 response, _ = self.controller.transact(request)
45 self.assertTrue(response is not None,
46 "Did not get echo reply")
47 self.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
48 'response is not echo_reply')
49 self.assertEqual(request.xid, response.xid,
50 'response xid != request xid')
51 self.assertEqual(request.data, response.data,
52 'response data != request data')
53
54class FeaturesRequest(base_tests.SimpleProtocol):
55 """
56 Test features_request to make sure we get a response
57
58 Does NOT test the contents; just that we get a response
59 """
60 def runTest(self):
61 request = ofp.message.features_request()
62 response,_ = self.controller.transact(request)
63 self.assertTrue(response is not None,
64 'Did not get features reply')
65
66class DefaultDrop(base_tests.SimpleDataPlane):
67 """
68 Check that an empty flowtable results in drops
69 """
70 def runTest(self):
71 in_port, = openflow_ports(1)
72 delete_all_flows(self.controller)
73
74 pkt = str(simple_tcp_packet())
75 self.dataplane.send(in_port, pkt)
76 verify_no_packet_in(self, pkt, None)
77 verify_packets(self, pkt, [])
78
79@disabled #FIXME ofdpa
80class OutputExact(base_tests.SimpleDataPlane):
81 """
82 Test output function for an exact-match flow
83
84 For each port A, adds a flow directing matching packets to that port.
85 Then, for all other ports B != A, verifies that sending a matching packet
86 to B results in an output to A.
87 """
88 def runTest(self):
89 ports = sorted(config["port_map"].keys())
90
91 delete_all_flows(self.controller)
92
93 parsed_pkt = simple_tcp_packet()
94 pkt = str(parsed_pkt)
95 match = packet_to_flow_match(self, parsed_pkt)
96
97 for out_port in ports:
98 request = ofp.message.flow_add(
99 table_id=test_param_get("table", 0),
100 cookie=42,
101 match=match,
102 instructions=[
103 ofp.instruction.apply_actions(
104 actions=[
105 ofp.action.output(
106 port=out_port,
107 max_len=ofp.OFPCML_NO_BUFFER)])],
108 buffer_id=ofp.OFP_NO_BUFFER,
109 priority=1000)
110
111 logging.info("Inserting flow sending matching packets to port %d", out_port)
112 self.controller.message_send(request)
113 do_barrier(self.controller)
114
115 for in_port in ports:
116 if in_port == out_port:
117 continue
118 logging.info("OutputExact test, ports %d to %d", in_port, out_port)
119 self.dataplane.send(in_port, pkt)
120 verify_packets(self, pkt, [out_port])
121
122@disabled #FIXME ofdpa
123class OutputWildcard(base_tests.SimpleDataPlane):
124 """
125 Test output function for a match-all (but not table-miss) flow
126
127 For each port A, adds a flow directing all packets to that port.
128 Then, for all other ports B != A, verifies that sending a packet
129 to B results in an output to A.
130 """
131 def runTest(self):
132 ports = sorted(config["port_map"].keys())
133
134 delete_all_flows(self.controller)
135
136 pkt = str(simple_tcp_packet())
137
138 for out_port in ports:
139 request = ofp.message.flow_add(
140 table_id=test_param_get("table", 0),
141 cookie=42,
142 instructions=[
143 ofp.instruction.apply_actions(
144 actions=[
145 ofp.action.output(
146 port=out_port,
147 max_len=ofp.OFPCML_NO_BUFFER)])],
148 buffer_id=ofp.OFP_NO_BUFFER,
149 priority=1000)
150
151 logging.info("Inserting flow sending all packets to port %d", out_port)
152 self.controller.message_send(request)
153 do_barrier(self.controller)
154
155 for in_port in ports:
156 if in_port == out_port:
157 continue
158 logging.info("OutputWildcard test, ports %d to %d", in_port, out_port)
159 self.dataplane.send(in_port, pkt)
160 verify_packets(self, pkt, [out_port])
161
162@disabled #FIXME ofdpa
163class PacketInExact(base_tests.SimpleDataPlane):
164 """
165 Test packet in function for an exact-match flow
166
167 Send a packet to each dataplane port and verify that a packet
168 in message is received from the controller for each
169 """
170 def runTest(self):
171 delete_all_flows(self.controller)
172
173 # required for OF-DPA to not drop packets
174 ofdpa_utils.installDefaultVlan(self.controller)
175
176 parsed_pkt = simple_tcp_packet()
177 pkt = str(parsed_pkt)
178
179 # NOTE: interally the switch adds a VLAN so the match needs to be with an explicit VLAN
180 parsed_vlan_pkt = simple_tcp_packet(dl_vlan_enable=True,
181 vlan_vid=ofdpa_utils.DEFAULT_VLAN,
182 vlan_pcp=0,
183 pktlen=104) # 4 less than we started with, because the way simple_tcp calc's length
184 match = packet_to_flow_match(self, parsed_vlan_pkt)
185 vlan_pkt = str(parsed_vlan_pkt)
186
187 request = ofp.message.flow_add(
188 table_id=ofdpa_utils.ACL_TABLE.table_id,
189 cookie=42,
190 match=match,
191 instructions=[
192 ofp.instruction.apply_actions(
193 actions=[
194 ofp.action.output(
195 port=ofp.OFPP_CONTROLLER,
196 max_len=ofp.OFPCML_NO_BUFFER)])],
197 buffer_id=ofp.OFP_NO_BUFFER,
198 priority=1000)
199
200 logging.info("Inserting flow sending matching packets to controller")
201 self.controller.message_send(request)
202 do_barrier(self.controller)
203
204 for of_port in config["port_map"].keys():
205 logging.info("PacketInExact test, port %d", of_port)
206 self.dataplane.send(of_port, pkt)
207 verify_packet_in(self, vlan_pkt, of_port, ofp.OFPR_ACTION)
208 verify_packets(self, pkt, [])
209
210@disabled #FIXME ofdpa
211class PacketInWildcard(base_tests.SimpleDataPlane):
212 # NOTE: interally the switch adds a VLAN so the match needs to be with an explicit VLAN
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 # required for OF-DPA to not drop packets
223 ofdpa_utils.installDefaultVlan(self.controller)
224
225 pkt = str(simple_tcp_packet())
226
227 # NOTE: interally the switch adds a VLAN so the match needs to be with an explicit VLAN
228 parsed_vlan_pkt = simple_tcp_packet(dl_vlan_enable=True,
229 vlan_vid=ofdpa_utils.DEFAULT_VLAN,
230 vlan_pcp=0,
231 pktlen=104) # 4 less than we started with, because the way simple_tcp calc's length
232 vlan_pkt = str(parsed_vlan_pkt)
233
234
235 request = ofp.message.flow_add(
236 table_id=ofdpa_utils.ACL_TABLE.table_id,
237 cookie=42,
238 instructions=[
239 ofp.instruction.apply_actions(
240 actions=[
241 ofp.action.output(
242 port=ofp.OFPP_CONTROLLER,
243 max_len=ofp.OFPCML_NO_BUFFER)])],
244 buffer_id=ofp.OFP_NO_BUFFER,
245 priority=1000)
246
247 logging.info("Inserting flow sending all packets to controller")
248 self.controller.message_send(request)
249 do_barrier(self.controller)
250
251 for of_port in config["port_map"].keys():
252 logging.info("PacketInWildcard test, port %d", of_port)
253 self.dataplane.send(of_port, pkt)
254 verify_packet_in(self, vlan_pkt, of_port, ofp.OFPR_ACTION)
255 verify_packets(self, pkt, [])
256
257@disabled #FIXME ofdpa
258class PacketInMiss(base_tests.SimpleDataPlane):
259 """
260 Test packet in function for a table-miss flow
261
262 Send a packet to each dataplane port and verify that a packet
263 in message is received from the controller for each
264 """
265 def runTest(self):
266 delete_all_flows(self.controller)
267
268 # required for OF-DPA to not drop packets
269 ofdpa_utils.installDefaultVlan(self.controller)
270
271 parsed_pkt = simple_tcp_packet()
272 pkt = str(parsed_pkt)
273
274 # NOTE: interally the switch adds a VLAN so the match needs to be with an explicit VLAN
275 parsed_vlan_pkt = simple_tcp_packet(dl_vlan_enable=True,
276 vlan_vid=ofdpa_utils.DEFAULT_VLAN,
277 vlan_pcp=0,
278 pktlen=104) # 4 less than we started with, because the way simple_tcp calc's length
279 vlan_pkt = str(parsed_vlan_pkt)
280
281 request = ofp.message.flow_add(
282 table_id=ofdpa_utils.ACL_TABLE.table_id,
283 cookie=42,
284 instructions=[
285 ofp.instruction.apply_actions(
286 actions=[
287 ofp.action.output(
288 port=ofp.OFPP_CONTROLLER,
289 max_len=ofp.OFPCML_NO_BUFFER)])],
290 buffer_id=ofp.OFP_NO_BUFFER,
291 priority=0)
292
293 logging.info("Inserting table-miss flow sending all packets to controller")
294 self.controller.message_send(request)
295 do_barrier(self.controller)
296
297 for of_port in config["port_map"].keys():
298 logging.info("PacketInMiss test, port %d", of_port)
299 self.dataplane.send(of_port, pkt)
300 verify_packet_in(self, vlan_pkt, of_port, ofp.OFPR_NO_MATCH)
301 verify_packets(self, pkt, [])
302
303class PacketOut(base_tests.SimpleDataPlane):
304 """
305 Test packet out function
306
307 Send packet out message to controller for each dataplane port and
308 verify the packet appears on the appropriate dataplane port
309 """
310 def runTest(self):
311 pkt = str(simple_tcp_packet())
312
313 for of_port in config["port_map"].keys():
314 msg = ofp.message.packet_out(
315 in_port=ofp.OFPP_CONTROLLER,
316 actions=[ofp.action.output(port=of_port)],
317 buffer_id=ofp.OFP_NO_BUFFER,
318 data=pkt)
319
320 logging.info("PacketOut test, port %d", of_port)
321 self.controller.message_send(msg)
322 verify_packets(self, pkt, [of_port])
323
324class FlowRemoveAll(base_tests.SimpleProtocol):
325 """
326 Remove all flows; required for almost all tests
327
328 Add a bunch of flows, remove them, and then make sure there are no flows left
329 This is an intentionally naive test to see if the baseline functionality works
330 and should be a precondition to any more complicated deletion test (e.g.,
331 delete_strict vs. delete)
332 """
333 def runTest(self):
334 for i in range(1,5):
335 logging.debug("Adding flow %d", i)
336 request = ofp.message.flow_add(
337 buffer_id=ofp.OFP_NO_BUFFER,
338 priority=i*1000)
339 self.controller.message_send(request)
340 do_barrier(self.controller)
341
342 delete_all_flows(self.controller)
343
344 logging.info("Sending flow stats request")
345 stats = get_flow_stats(self, ofp.match())
346 self.assertEqual(len(stats), 0, "Expected empty flow stats reply")
347
348
349## Multipart messages
350
351class DescStats(base_tests.SimpleProtocol):
352 """
353 Switch description multipart transaction
354
355 Only verifies we get a single reply.
356 """
357 def runTest(self):
358 request = ofp.message.desc_stats_request()
359 logging.info("Sending desc stats request")
360 response, _ = self.controller.transact(request)
361 self.assertTrue(response != None, "No response to desc stats request")
362 logging.info(response.show())
363 self.assertEquals(response.flags, 0, "Unexpected bit set in desc stats reply flags")
364
365class FlowStats(base_tests.SimpleProtocol):
366 """
367 Flow stats multipart transaction
368
369 Only verifies we get a reply.
370 """
371 def runTest(self):
372 logging.info("Sending flow stats request")
373 stats = get_flow_stats(self, ofp.match())
374 logging.info("Received %d flow stats entries", len(stats))
375 for entry in stats:
376 logging.info(entry.show())
377
378class AggregateStats(base_tests.SimpleProtocol):
379 """
380 Aggregate flow stats multipart transaction
381
382 Only verifies we get a single reply.
383 """
384 def runTest(self):
385 request = ofp.message.aggregate_stats_request(
386 table_id=ofp.OFPTT_ALL,
387 out_port=ofp.OFPP_ANY,
388 out_group=ofp.OFPG_ANY,
389 cookie=0,
390 cookie_mask=0)
391 logging.info("Sending aggregate flow stats request")
392 response, _ = self.controller.transact(request)
393 self.assertTrue(response != None, "No response to aggregate stats request")
394 logging.info(response.show())
395 self.assertEquals(response.flags, 0, "Unexpected bit set in aggregate stats reply flags")
396
397class TableStats(base_tests.SimpleProtocol):
398 """
399 Table stats multipart transaction
400
401 Only verifies we get a reply.
402 """
403 def runTest(self):
404 logging.info("Sending table stats request")
405 stats = get_stats(self, ofp.message.table_stats_request())
406 logging.info("Received %d table stats entries", len(stats))
407 for entry in stats:
408 logging.info(entry.show())
409
410class PortStats(base_tests.SimpleProtocol):
411 """
412 Port stats multipart transaction
413
414 Only verifies we get a reply.
415 """
416 def runTest(self):
417 request = ofp.message.port_stats_request(port_no=ofp.OFPP_ANY)
418 logging.info("Sending port stats request")
419 stats = get_stats(self, request)
420 logging.info("Received %d port stats entries", len(stats))
421 for entry in stats:
422 logging.info(entry.show())
423
424class QueueStats(base_tests.SimpleProtocol):
425 """
426 Queue stats multipart transaction
427
428 Only verifies we get a reply.
429 """
430 def runTest(self):
431 request = ofp.message.queue_stats_request(port_no=ofp.OFPP_ANY,
432 queue_id=ofp.OFPQ_ALL)
433 logging.info("Sending queue stats request")
434 stats = get_stats(self, request)
435 logging.info("Received %d queue stats entries", len(stats))
436 for entry in stats:
437 logging.info(entry.show())
438
439class GroupStats(base_tests.SimpleProtocol):
440 """
441 Group stats multipart transaction
442
443 Only verifies we get a reply.
444 """
445 def runTest(self):
446 request = ofp.message.group_stats_request(group_id=ofp.OFPG_ALL)
447 logging.info("Sending group stats request")
448 stats = get_stats(self, request)
449 logging.info("Received %d group stats entries", len(stats))
450 for entry in stats:
451 logging.info(entry.show())
452
453class GroupDescStats(base_tests.SimpleProtocol):
454 """
455 Group description multipart transaction
456
457 Only verifies we get a reply.
458 """
459 def runTest(self):
460 request = ofp.message.group_desc_stats_request()
461 logging.info("Sending group desc stats request")
462 stats = get_stats(self, request)
463 logging.info("Received %d group desc stats entries", len(stats))
464 for entry in stats:
465 logging.info(entry.show())
466
467@disabled #FIXME ofdpa
468class GroupFeaturesStats(base_tests.SimpleProtocol):
469 """
470 Group features multipart transaction
471
472 Only verifies we get a single reply.
473 """
474 def runTest(self):
475 request = ofp.message.group_features_stats_request()
476 logging.info("Sending group features stats request")
477 response, _ = self.controller.transact(request)
478 self.assertTrue(response != None, "No response to group features stats request")
479 logging.info(response.show())
480 self.assertEquals(response.flags, 0, "Unexpected bit set in group features stats reply flags")
481
482@disabled #FIXME ofdpa
483class MeterStats(base_tests.SimpleProtocol):
484 """
485 Meter stats multipart transaction
486
487 Only verifies we get a reply.
488 """
489 def runTest(self):
490 request = ofp.message.meter_stats_request(meter_id=ofp.OFPM_ALL)
491 logging.info("Sending meter stats request")
492 stats = get_stats(self, request)
493 logging.info("Received %d meter stats entries", len(stats))
494 for entry in stats:
495 logging.info(entry.show())
496
497@disabled #FIXME ofdpa
498class MeterConfigStats(base_tests.SimpleProtocol):
499 """
500 Meter config multipart transaction
501
502 Only verifies we get a reply.
503 """
504 def runTest(self):
505 request = ofp.message.meter_config_stats_request(meter_id=ofp.OFPM_ALL)
506 logging.info("Sending meter config stats request")
507 stats = get_stats(self, request)
508 logging.info("Received %d meter config stats entries", len(stats))
509 for entry in stats:
510 logging.info(entry.show())
511
512@disabled #FIXME ofdpa
513class MeterFeaturesStats(base_tests.SimpleProtocol):
514 """
515 Meter features multipart transaction
516
517 Only verifies we get a single reply.
518 """
519 def runTest(self):
520 request = ofp.message.meter_features_stats_request()
521 logging.info("Sending meter features stats request")
522 response, _ = self.controller.transact(request)
523 self.assertTrue(response != None, "No response to meter features stats request")
524 logging.info(response.show())
525 self.assertEquals(response.flags, 0, "Unexpected bit set in meter features stats reply flags")
526
527@disabled # pyloxi does not yet support table features
528class TableFeaturesStats(base_tests.SimpleProtocol):
529 """
530 Table features multipart transaction
531
532 Only verifies we get a reply.
533 """
534 def runTest(self):
535 logging.info("Sending table features stats request")
536 stats = get_stats(self, ofp.message.table_features_stats_request())
537 logging.info("Received %d table features stats entries", len(stats))
538 for entry in stats:
539 logging.info(entry.show())
540
541class PortDescStats(base_tests.SimpleProtocol):
542 """
543 Port description multipart transaction
544
545 Only verifies we get a reply.
546 """
547 def runTest(self):
548 logging.info("Sending port desc stats request")
549 stats = get_stats(self, ofp.message.port_desc_stats_request())
550 logging.info("Received %d port desc stats entries", len(stats))
551 for entry in stats:
552 logging.info(entry.show())
553
554class PortConfigMod(base_tests.SimpleProtocol):
555 """
556 Modify a bit in port config and verify changed
557
558 Get the switch configuration, modify the port configuration
559 and write it back; get the config again and verify changed.
560 Then set it back to the way it was.
561 """
562
563 def runTest(self):
564 logging.info("Running " + str(self))
565 for of_port, _ in config["port_map"].items(): # Grab first port
566 break
567
568 (_, config1, _) = \
569 port_config_get(self.controller, of_port)
570 self.assertTrue(config is not None, "Did not get port config")
571
572 logging.debug("OFPPC_NO_PACKET_IN bit port " + str(of_port) + " is now " +
573 str(config1 & ofp.OFPPC_NO_PACKET_IN))
574
575 rv = port_config_set(self.controller, of_port,
576 config1 ^ ofp.OFPPC_NO_PACKET_IN,
577 ofp.OFPPC_NO_PACKET_IN)
578 self.assertTrue(rv != -1, "Error sending port mod")
579
580 # Verify change took place with same feature request
581 (_, config2, _) = port_config_get(self.controller, of_port)
582 self.assertTrue(config2 is not None, "Did not get port config2")
583 logging.debug("OFPPC_NO_PACKET_IN bit port " + str(of_port) + " is now " +
584 str(config2 & ofp.OFPPC_NO_PACKET_IN))
585 self.assertTrue(config2 & ofp.OFPPC_NO_PACKET_IN !=
586 config1 & ofp.OFPPC_NO_PACKET_IN,
587 "Bit change did not take")
588 # Set it back
589 rv = port_config_set(self.controller, of_port, config1,
590 ofp.OFPPC_NO_PACKET_IN)
591 self.assertTrue(rv != -1, "Error sending port mod")
592
593@disabled #FIXME ofdpa
594class AsyncConfigGet(base_tests.SimpleProtocol):
595 """
596 Verify initial async config
597
598 Other tests rely on connections starting with these values.
599 """
600
601 def runTest(self):
602 logging.info("Sending get async config request")
603 response, _ = self.controller.transact(ofp.message.async_get_request())
604 self.assertTrue(response != None, "No response to get async config request")
605 logging.info(response.show())
606 self.assertEquals(response.packet_in_mask_equal_master & 0x07, 0x07)
607 self.assertEquals(response.port_status_mask_equal_master & 0x07, 0x07)
608 self.assertEquals(response.flow_removed_mask_equal_master & 0x0f, 0x0f)