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