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