blob: 473bda2b65ba7c84e47321d71653b4cbe72f438d [file] [log] [blame]
Dan Talayco89d57342010-06-07 16:24:59 -07001"""
2Flow stats test case.
3Similar to Flow stats test case in the perl test harness.
4
5"""
6
7import logging
8
9import unittest
10import random
11
12import oftest.controller as controller
13import oftest.cstruct as ofp
14import oftest.message as message
15import oftest.dataplane as dataplane
16import oftest.action as action
17import oftest.parse as parse
18import basic
19
20from testutils import *
21from time import sleep
22
23#@var port_map Local copy of the configuration map from OF port
24# numbers to OS interfaces
25pa_port_map = None
26#@var pa_logger Local logger object
27pa_logger = None
28#@var pa_config Local copy of global configuration data
29pa_config = None
30
Ken Chiang620bdcc2012-03-23 12:52:07 -070031# TODO: ovs has problems with VLAN id?
32WILDCARD_VALUES = [ofp.OFPFW_IN_PORT,
33 # ofp.OFPFW_DL_VLAN,
34 ofp.OFPFW_DL_SRC,
35 ofp.OFPFW_DL_DST,
36 ofp.OFPFW_DL_TYPE,
37 ofp.OFPFW_NW_PROTO,
38 ofp.OFPFW_TP_SRC,
39 ofp.OFPFW_TP_DST,
40 0x3F << ofp.OFPFW_NW_SRC_SHIFT,
41 0x3F << ofp.OFPFW_NW_DST_SHIFT,
42 ofp.OFPFW_DL_VLAN_PCP,
43 ofp.OFPFW_NW_TOS]
44
Dan Talayco89d57342010-06-07 16:24:59 -070045def test_set_init(config):
46 """
47 Set up function for packet action test classes
48
49 @param config The configuration dictionary; see oft
50 """
51
52 global pa_port_map
53 global pa_logger
54 global pa_config
55
56 pa_logger = logging.getLogger("pkt_act")
57 pa_logger.info("Initializing test set")
58 pa_port_map = config["port_map"]
59 pa_config = config
60
Ken Chiang620bdcc2012-03-23 12:52:07 -070061class SingleFlowStats(basic.SimpleDataPlane):
Dan Talayco89d57342010-06-07 16:24:59 -070062 """
63 Verify flow stats are properly retrieved.
64
65 Generate a packet
Ken Chiang620bdcc2012-03-23 12:52:07 -070066 Generate and install a matching flow
67 Send the packet
68 Send a flow stats request to match the flow and retrieve stats
69 Verify that the packet counter has incremented
Dan Talayco89d57342010-06-07 16:24:59 -070070 """
Ken Chiang620bdcc2012-03-23 12:52:07 -070071
72 def verifyStats(self, match, out_port, test_timeout, packet_count):
73 stat_req = message.flow_stats_request()
74 stat_req.match = match
75 stat_req.table_id = 0xff
76 stat_req.out_port = out_port
77
78 all_packets_received = 0
79 for i in range(0,test_timeout):
80 pa_logger.info("Sending stats request")
81 rv = self.controller.message_send(stat_req)
82 self.assertTrue(rv != -1, "Error sending flow stat req")
83 do_barrier(self.controller)
84
85 (response, raw) = self.controller.poll(ofp.OFPT_STATS_REPLY, 2)
86 self.assertTrue(len(response.stats) == 1, "Did not receive flow stats reply")
87 for obj in response.stats:
88 # TODO: pad1 and pad2 fields may be nonzero, is this a bug?
89 # for now, just clear them so the assert is simpler
90 #obj.match.pad1 = 0
91 #obj.match.pad2 = [0, 0]
92 #self.assertEqual(match, obj.match,
93 # "Matches do not match")
94 pa_logger.info("Received " + str(obj.packet_count) + " packets")
95 if obj.packet_count == packet_count:
96 all_packets_received = 1
97
98 if all_packets_received:
99 break
100 sleep(1)
101
102 self.assertTrue(all_packets_received,
103 "Packet count does not match number sent")
104
Dan Talayco89d57342010-06-07 16:24:59 -0700105 def runTest(self):
106 global pa_port_map
107
Ken Chiang620bdcc2012-03-23 12:52:07 -0700108 # TODO: set from command-line parameter
109 test_timeout = 60
110
Dan Talayco89d57342010-06-07 16:24:59 -0700111 of_ports = pa_port_map.keys()
112 of_ports.sort()
113 self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
114
115 rc = delete_all_flows(self.controller, pa_logger)
116 self.assertEqual(rc, 0, "Failed to delete all flows")
117
Ken Chiang620bdcc2012-03-23 12:52:07 -0700118 # build packet
Dan Talayco89d57342010-06-07 16:24:59 -0700119 pkt = simple_tcp_packet()
120 match = parse.packet_to_flow_match(pkt)
121 match.wildcards &= ~ofp.OFPFW_IN_PORT
122 self.assertTrue(match is not None,
123 "Could not generate flow match from pkt")
124 act = action.action_output()
125
Ken Chiang620bdcc2012-03-23 12:52:07 -0700126 # build flow
Dan Talayco39bf6912010-07-08 14:17:52 -0700127 ingress_port = of_ports[0];
128 egress_port = of_ports[1];
Dan Talayco89d57342010-06-07 16:24:59 -0700129 pa_logger.info("Ingress " + str(ingress_port) +
130 " to egress " + str(egress_port))
Ken Chiang620bdcc2012-03-23 12:52:07 -0700131 match.in_port = ingress_port
132 flow_mod_msg = message.flow_mod()
133 flow_mod_msg.match = match
134 flow_mod_msg.cookie = random.randint(0,9007199254740992)
135 flow_mod_msg.buffer_id = 0xffffffff
136 flow_mod_msg.idle_timeout = 0
137 flow_mod_msg.hard_timeout = 0
138 act.port = egress_port
139 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
140
141 # send flow
142 pa_logger.info("Inserting flow")
143 rv = self.controller.message_send(flow_mod_msg)
144 self.assertTrue(rv != -1, "Error installing flow mod")
145 do_barrier(self.controller)
146
147 # no packets sent, so zero packet count
148 self.verifyStats(match, ofp.OFPP_NONE, test_timeout, 0)
149
150 # send packet N times
151 num_sends = random.randint(10,20)
152 pa_logger.info("Sending " + str(num_sends) + " test packets")
153 for i in range(0,num_sends):
154 pa_logger.info("Sending packet to dp port " +
155 str(ingress_port))
156 self.dataplane.send(ingress_port, str(pkt))
157 (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=test_timeout)
158 self.assertTrue(rcv_pkt is not None, "Did not receive packet")
159 pa_logger.debug("Packet len " + str(len(pkt)) + " in on " +
160 str(rcv_port))
161 self.assertEqual(rcv_port, egress_port, "Unexpected receive port")
162 for j in range(0,test_timeout):
163 if str(pkt) == str(rcv_pkt):
164 break
165 sleep(1)
166 self.assertTrue(j < test_timeout,
167 'Timeout sending packets for flow stats test')
168
169 self.verifyStats(match, ofp.OFPP_NONE, test_timeout, num_sends)
170 self.verifyStats(match, egress_port, test_timeout, num_sends)
171 for wc in WILDCARD_VALUES:
172 match.wildcards = wc
173 self.verifyStats(match, egress_port, test_timeout, num_sends)
174
175
176class TwoFlowStats(basic.SimpleDataPlane):
177 """
178 Verify flow stats are properly retrieved.
179
180 Generate two packets and install two matching flows
181 Send some number of packets
182 Send a flow stats request to match the flows and retrieve stats
183 Verify that the packet counter has incremented
184
185 TODO: add a third flow, and then configure the match to exclude
186 that flow?
187 """
188
189 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
190 match = parse.packet_to_flow_match(pkt)
191 match.wildcards &= ~ofp.OFPFW_IN_PORT
192 self.assertTrue(match is not None,
193 "Could not generate flow match from pkt")
Dan Talayco89d57342010-06-07 16:24:59 -0700194 match.in_port = ingress_port
195
196 flow_mod_msg = message.flow_mod()
197 flow_mod_msg.match = match
198 flow_mod_msg.cookie = random.randint(0,9007199254740992)
199 flow_mod_msg.buffer_id = 0xffffffff
Ken Chiang620bdcc2012-03-23 12:52:07 -0700200 flow_mod_msg.idle_timeout = 0
201 flow_mod_msg.hard_timeout = 0
202 act = action.action_output()
Dan Talayco89d57342010-06-07 16:24:59 -0700203 act.port = egress_port
204 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
Dan Talayco89d57342010-06-07 16:24:59 -0700205
Ken Chiang620bdcc2012-03-23 12:52:07 -0700206 pa_logger.info("Ingress " + str(ingress_port) +
207 " to egress " + str(egress_port))
Dan Talayco89d57342010-06-07 16:24:59 -0700208
Ken Chiang620bdcc2012-03-23 12:52:07 -0700209 return flow_mod_msg
Dan Talayco89d57342010-06-07 16:24:59 -0700210
Ken Chiang620bdcc2012-03-23 12:52:07 -0700211 def sendPacket(self, pkt, ingress_port, egress_port, test_timeout):
Dan Talayco89d57342010-06-07 16:24:59 -0700212 pa_logger.info("Sending packet to dp port " +
213 str(ingress_port))
214 self.dataplane.send(ingress_port, str(pkt))
Ken Chiang620bdcc2012-03-23 12:52:07 -0700215 (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=test_timeout)
Dan Talayco89d57342010-06-07 16:24:59 -0700216 self.assertTrue(rcv_pkt is not None, "Did not receive packet")
217 pa_logger.debug("Packet len " + str(len(pkt)) + " in on " +
218 str(rcv_port))
219 self.assertEqual(rcv_port, egress_port, "Unexpected receive port")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700220 for j in range(0,test_timeout):
221 if str(pkt) == str(rcv_pkt):
222 break
223 sleep(1)
224 self.assertTrue(j < test_timeout,
225 'Timeout sending packets for flow stats test')
226
227 def verifyStats(self, match, out_port, test_timeout, packet_count):
228 stat_req = message.flow_stats_request()
229 stat_req.match = match
230 stat_req.table_id = 0xff
231 stat_req.out_port = out_port
232
233 all_packets_received = 0
234 for i in range(0,test_timeout):
235 pa_logger.info("Sending stats request")
236 rv = self.controller.message_send(stat_req)
237 self.assertTrue(rv != -1, "Error sending flow stat req")
238 do_barrier(self.controller)
239
240 (response, raw) = self.controller.poll(ofp.OFPT_STATS_REPLY, 2)
241 self.assertTrue(len(response.stats) >= 1, "Did not receive flow stats reply")
242 total_packets = 0
243 for obj in response.stats:
244 # TODO: pad1 and pad2 fields may be nonzero, is this a bug?
245 # for now, just clear them so the assert is simpler
246 #obj.match.pad1 = 0
247 #obj.match.pad2 = [0, 0]
248 #self.assertEqual(match, obj.match,
249 # "Matches do not match")
250 pa_logger.info("Received " + str(obj.packet_count) + " packets")
251 total_packets += obj.packet_count
252 if total_packets == packet_count:
253 all_packets_received = 1
254 break
255 sleep(1)
256
257 self.assertTrue(all_packets_received,
258 "Packet count does not match number sent")
259
260 def runTest(self):
261 global pa_port_map
262
263 # TODO: set from command-line parameter
264 test_timeout = 60
265
266 of_ports = pa_port_map.keys()
267 of_ports.sort()
268 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
269 ingress_port = of_ports[0];
270 egress_port1 = of_ports[1];
271 egress_port2 = of_ports[2];
272
273 rc = delete_all_flows(self.controller, pa_logger)
274 self.assertEqual(rc, 0, "Failed to delete all flows")
275
276 pkt1 = simple_tcp_packet()
277 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
278
279 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
280 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
281
282 pa_logger.info("Inserting flow1")
283 rv = self.controller.message_send(flow_mod_msg1)
284 self.assertTrue(rv != -1, "Error installing flow mod")
285 pa_logger.info("Inserting flow2")
286 rv = self.controller.message_send(flow_mod_msg2)
287 self.assertTrue(rv != -1, "Error installing flow mod")
Dan Talayco89d57342010-06-07 16:24:59 -0700288 do_barrier(self.controller)
289
Ken Chiang620bdcc2012-03-23 12:52:07 -0700290 num_pkt1s = random.randint(10,30)
291 pa_logger.info("Sending " + str(num_pkt1s) + " pkt1s")
292 num_pkt2s = random.randint(10,30)
293 pa_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
294 for i in range(0,num_pkt1s):
295 self.sendPacket(pkt1, ingress_port, egress_port1, test_timeout)
296 for i in range(0,num_pkt2s):
297 self.sendPacket(pkt2, ingress_port, egress_port2, test_timeout)
298
299 match1 = parse.packet_to_flow_match(pkt1)
300 self.verifyStats(match1, ofp.OFPP_NONE, test_timeout, num_pkt1s)
301 match2 = parse.packet_to_flow_match(pkt2)
302 self.verifyStats(match2, ofp.OFPP_NONE, test_timeout, num_pkt2s)
303 match1.wildcards |= ofp.OFPFW_DL_SRC
304 self.verifyStats(match1, ofp.OFPP_NONE, test_timeout,
305 num_pkt1s+num_pkt2s)
306 # TODO: sweep through the wildcards to verify matching?
307
308
309class AggregateStats(basic.SimpleDataPlane):
310 """
311 Verify aggregate flow stats are properly retrieved.
312
313 Generate two packets
314 Generate and install two matching flows
315 Send an aggregate stats request
316 Verify that aggregate stats are correct
317 Also verify out_port filtering
318 """
319
320 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
321 match = parse.packet_to_flow_match(pkt)
322 match.wildcards &= ~ofp.OFPFW_IN_PORT
323 self.assertTrue(match is not None,
324 "Could not generate flow match from pkt")
325 match.in_port = ingress_port
326
327 flow_mod_msg = message.flow_mod()
328 flow_mod_msg.match = match
329 flow_mod_msg.cookie = random.randint(0,9007199254740992)
330 flow_mod_msg.buffer_id = 0xffffffff
331 flow_mod_msg.idle_timeout = 0
332 flow_mod_msg.hard_timeout = 0
333 act = action.action_output()
334 act.port = egress_port
335 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
336
337 pa_logger.info("Ingress " + str(ingress_port) +
338 " to egress " + str(egress_port))
339
340 return flow_mod_msg
341
342 def sendPacket(self, pkt, ingress_port, egress_port, test_timeout):
343 pa_logger.info("Sending packet to dp port " +
344 str(ingress_port))
345 self.dataplane.send(ingress_port, str(pkt))
346 (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=test_timeout)
347 self.assertTrue(rcv_pkt is not None, "Did not receive packet")
348 pa_logger.debug("Packet len " + str(len(pkt)) + " in on " +
349 str(rcv_port))
350 self.assertEqual(rcv_port, egress_port, "Unexpected receive port")
351 for j in range(0,test_timeout):
352 if str(pkt) == str(rcv_pkt):
353 break
354 sleep(1)
355 self.assertTrue(j < test_timeout,
356 'Timeout sending packets for flow stats test')
357
358 def verifyAggFlowStats(self, match, out_port, test_timeout,
359 flow_count, packet_count):
360 stat_req = message.aggregate_stats_request()
361 stat_req.match = match
362 stat_req.table_id = 0xff
363 stat_req.out_port = out_port
364
365 all_packets_received = 0
366 for i in range(0,test_timeout):
367 pa_logger.info("Sending stats request")
368 rv = self.controller.message_send(stat_req)
369 self.assertTrue(rv != -1, "Error sending flow stat req")
370 do_barrier(self.controller)
371
372 (response, raw) = self.controller.poll(ofp.OFPT_STATS_REPLY, 2)
373 self.assertTrue(len(response.stats) == 1, "Did not receive flow stats reply")
374 for obj in response.stats:
375 self.assertTrue(obj.flow_count == flow_count,
376 "Flow count " + str(obj.flow_count) +
377 " does not match expected " + str(flow_count))
378 pa_logger.info("Received " + str(obj.packet_count) + " packets")
379 if obj.packet_count == packet_count:
380 all_packets_received = 1
381
382 if all_packets_received:
383 break
384 sleep(1)
385
386 self.assertTrue(all_packets_received,
387 "Packet count does not match number sent")
388
389 def runTest(self):
390 global pa_port_map
391
392 # TODO: set from command-line parameter
393 test_timeout = 60
394
395 of_ports = pa_port_map.keys()
396 of_ports.sort()
397 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
398 ingress_port = of_ports[0];
399 egress_port1 = of_ports[1];
400 egress_port2 = of_ports[2];
401
402 rc = delete_all_flows(self.controller, pa_logger)
403 self.assertEqual(rc, 0, "Failed to delete all flows")
404
405 pkt1 = simple_tcp_packet()
406 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
407
408 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
409 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
410
411 pa_logger.info("Inserting flow1")
412 rv = self.controller.message_send(flow_mod_msg1)
413 self.assertTrue(rv != -1, "Error installing flow mod")
414 pa_logger.info("Inserting flow2")
415 rv = self.controller.message_send(flow_mod_msg2)
416 self.assertTrue(rv != -1, "Error installing flow mod")
417 do_barrier(self.controller)
418
419 num_pkt1s = random.randint(10,30)
420 pa_logger.info("Sending " + str(num_pkt1s) + " pkt1s")
421 num_pkt2s = random.randint(10,30)
422 pa_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
423 for i in range(0,num_pkt1s):
424 self.sendPacket(pkt1, ingress_port, egress_port1, test_timeout)
425 for i in range(0,num_pkt2s):
426 self.sendPacket(pkt2, ingress_port, egress_port2, test_timeout)
427
428 # loop on flow stats request until timeout
429 match = parse.packet_to_flow_match(pkt1)
430 match.wildcards |= ofp.OFPFW_DL_SRC
431 self.verifyAggFlowStats(match, ofp.OFPP_NONE, test_timeout,
432 2, num_pkt1s+num_pkt2s)
433
434 # out_port filter for egress_port1
435 self.verifyAggFlowStats(match, egress_port1, test_timeout,
436 1, num_pkt1s)
437
438 # out_port filter for egress_port1
439 self.verifyAggFlowStats(match, egress_port2, test_timeout,
440 1, num_pkt2s)