blob: 91c67a61ad576baa3b32d7145f195159de27ca8f [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
Ken Chiangfb593e72012-03-28 17:19:13 -070023#@var fs_port_map Local copy of the configuration map from OF port
Dan Talayco89d57342010-06-07 16:24:59 -070024# numbers to OS interfaces
Ken Chiangfb593e72012-03-28 17:19:13 -070025fs_port_map = None
26#@var fs_logger Local logger object
27fs_logger = None
28#@var fs_config Local copy of global configuration data
29fs_config = None
Dan Talayco89d57342010-06-07 16:24:59 -070030
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
Ken Chiangfb593e72012-03-28 17:19:13 -070052 global fs_port_map
53 global fs_logger
54 global fs_config
Dan Talayco89d57342010-06-07 16:24:59 -070055
Ken Chiangfb593e72012-03-28 17:19:13 -070056 fs_logger = logging.getLogger("flow_stats")
57 fs_logger.info("Initializing test set")
58 fs_port_map = config["port_map"]
59 fs_config = config
Dan Talayco89d57342010-06-07 16:24:59 -070060
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):
Ken Chiangfb593e72012-03-28 17:19:13 -070080 fs_logger.info("Sending stats request")
81 response, pkt = self.controller.transact(stat_req,
82 timeout=test_timeout)
83 self.assertTrue(response is not None,
84 "No response to stats request")
85 self.assertTrue(len(response.stats) == 1,
86 "Did not receive flow stats reply")
Ken Chiang620bdcc2012-03-23 12:52:07 -070087 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")
Ken Chiangfb593e72012-03-28 17:19:13 -070094 fs_logger.info("Received " + str(obj.packet_count) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -070095 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):
Ken Chiangfb593e72012-03-28 17:19:13 -0700106 global fs_port_map
Dan Talayco89d57342010-06-07 16:24:59 -0700107
Ken Chiang620bdcc2012-03-23 12:52:07 -0700108 # TODO: set from command-line parameter
109 test_timeout = 60
110
Ken Chiangfb593e72012-03-28 17:19:13 -0700111 of_ports = fs_port_map.keys()
Dan Talayco89d57342010-06-07 16:24:59 -0700112 of_ports.sort()
113 self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
114
Ken Chiangfb593e72012-03-28 17:19:13 -0700115 rc = delete_all_flows(self.controller, fs_logger)
Dan Talayco89d57342010-06-07 16:24:59 -0700116 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];
Ken Chiangfb593e72012-03-28 17:19:13 -0700129 fs_logger.info("Ingress " + str(ingress_port) +
Dan Talayco89d57342010-06-07 16:24:59 -0700130 " 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
Ken Chiangfb593e72012-03-28 17:19:13 -0700142 fs_logger.info("Inserting flow")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700143 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)
Ken Chiangfb593e72012-03-28 17:19:13 -0700152 fs_logger.info("Sending " + str(num_sends) + " test packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700153 for i in range(0,num_sends):
Ken Chiangfb593e72012-03-28 17:19:13 -0700154 fs_logger.info("Sending packet to dp port " +
Ken Chiang620bdcc2012-03-23 12:52:07 -0700155 str(ingress_port))
156 self.dataplane.send(ingress_port, str(pkt))
Dan Talaycoaff26c82012-03-25 15:06:26 -0700157 (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=
158 test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700159 self.assertTrue(rcv_pkt is not None, "Did not receive packet")
Ken Chiangfb593e72012-03-28 17:19:13 -0700160 fs_logger.debug("Packet len " + str(len(pkt)) + " in on " +
Ken Chiang620bdcc2012-03-23 12:52:07 -0700161 str(rcv_port))
162 self.assertEqual(rcv_port, egress_port, "Unexpected receive port")
163 for j in range(0,test_timeout):
164 if str(pkt) == str(rcv_pkt):
165 break
166 sleep(1)
167 self.assertTrue(j < test_timeout,
168 'Timeout sending packets for flow stats test')
169
170 self.verifyStats(match, ofp.OFPP_NONE, test_timeout, num_sends)
171 self.verifyStats(match, egress_port, test_timeout, num_sends)
172 for wc in WILDCARD_VALUES:
173 match.wildcards = wc
174 self.verifyStats(match, egress_port, test_timeout, num_sends)
175
176
177class TwoFlowStats(basic.SimpleDataPlane):
178 """
179 Verify flow stats are properly retrieved.
180
181 Generate two packets and install two matching flows
182 Send some number of packets
183 Send a flow stats request to match the flows and retrieve stats
184 Verify that the packet counter has incremented
185
186 TODO: add a third flow, and then configure the match to exclude
187 that flow?
188 """
189
190 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
191 match = parse.packet_to_flow_match(pkt)
192 match.wildcards &= ~ofp.OFPFW_IN_PORT
193 self.assertTrue(match is not None,
194 "Could not generate flow match from pkt")
Dan Talayco89d57342010-06-07 16:24:59 -0700195 match.in_port = ingress_port
196
197 flow_mod_msg = message.flow_mod()
198 flow_mod_msg.match = match
199 flow_mod_msg.cookie = random.randint(0,9007199254740992)
200 flow_mod_msg.buffer_id = 0xffffffff
Ken Chiang620bdcc2012-03-23 12:52:07 -0700201 flow_mod_msg.idle_timeout = 0
202 flow_mod_msg.hard_timeout = 0
203 act = action.action_output()
Dan Talayco89d57342010-06-07 16:24:59 -0700204 act.port = egress_port
205 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
Dan Talayco89d57342010-06-07 16:24:59 -0700206
Ken Chiangfb593e72012-03-28 17:19:13 -0700207 fs_logger.info("Ingress " + str(ingress_port) +
Ken Chiang620bdcc2012-03-23 12:52:07 -0700208 " to egress " + str(egress_port))
Dan Talayco89d57342010-06-07 16:24:59 -0700209
Ken Chiang620bdcc2012-03-23 12:52:07 -0700210 return flow_mod_msg
Dan Talayco89d57342010-06-07 16:24:59 -0700211
Ken Chiang620bdcc2012-03-23 12:52:07 -0700212 def sendPacket(self, pkt, ingress_port, egress_port, test_timeout):
Ken Chiangfb593e72012-03-28 17:19:13 -0700213 fs_logger.info("Sending packet to dp port " +
Dan Talayco89d57342010-06-07 16:24:59 -0700214 str(ingress_port))
215 self.dataplane.send(ingress_port, str(pkt))
Dan Talaycoaff26c82012-03-25 15:06:26 -0700216 (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=
217 test_timeout)
Dan Talayco89d57342010-06-07 16:24:59 -0700218 self.assertTrue(rcv_pkt is not None, "Did not receive packet")
Ken Chiangfb593e72012-03-28 17:19:13 -0700219 fs_logger.debug("Packet len " + str(len(pkt)) + " in on " +
Dan Talayco89d57342010-06-07 16:24:59 -0700220 str(rcv_port))
221 self.assertEqual(rcv_port, egress_port, "Unexpected receive port")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700222 for j in range(0,test_timeout):
223 if str(pkt) == str(rcv_pkt):
224 break
225 sleep(1)
226 self.assertTrue(j < test_timeout,
227 'Timeout sending packets for flow stats test')
228
229 def verifyStats(self, match, out_port, test_timeout, packet_count):
230 stat_req = message.flow_stats_request()
231 stat_req.match = match
232 stat_req.table_id = 0xff
233 stat_req.out_port = out_port
234
235 all_packets_received = 0
236 for i in range(0,test_timeout):
Ken Chiangfb593e72012-03-28 17:19:13 -0700237 fs_logger.info("Sending stats request")
238 response, pkt = self.controller.transact(stat_req,
239 timeout=test_timeout)
240 self.assertTrue(response is not None,
241 "No response to stats request")
Dan Talaycoaff26c82012-03-25 15:06:26 -0700242 self.assertTrue(len(response.stats) >= 1,
243 "Did not receive flow stats reply")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700244 total_packets = 0
245 for obj in response.stats:
246 # TODO: pad1 and pad2 fields may be nonzero, is this a bug?
247 # for now, just clear them so the assert is simpler
248 #obj.match.pad1 = 0
249 #obj.match.pad2 = [0, 0]
250 #self.assertEqual(match, obj.match,
251 # "Matches do not match")
Ken Chiangfb593e72012-03-28 17:19:13 -0700252 fs_logger.info("Received " + str(obj.packet_count) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700253 total_packets += obj.packet_count
254 if total_packets == packet_count:
255 all_packets_received = 1
256 break
257 sleep(1)
258
259 self.assertTrue(all_packets_received,
260 "Packet count does not match number sent")
261
262 def runTest(self):
Ken Chiangfb593e72012-03-28 17:19:13 -0700263 global fs_port_map
Ken Chiang620bdcc2012-03-23 12:52:07 -0700264
265 # TODO: set from command-line parameter
266 test_timeout = 60
267
Ken Chiangfb593e72012-03-28 17:19:13 -0700268 of_ports = fs_port_map.keys()
Ken Chiang620bdcc2012-03-23 12:52:07 -0700269 of_ports.sort()
270 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
271 ingress_port = of_ports[0];
272 egress_port1 = of_ports[1];
273 egress_port2 = of_ports[2];
274
Ken Chiangfb593e72012-03-28 17:19:13 -0700275 rc = delete_all_flows(self.controller, fs_logger)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700276 self.assertEqual(rc, 0, "Failed to delete all flows")
277
278 pkt1 = simple_tcp_packet()
279 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
280
281 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
282 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
283
Ken Chiangfb593e72012-03-28 17:19:13 -0700284 fs_logger.info("Inserting flow1")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700285 rv = self.controller.message_send(flow_mod_msg1)
286 self.assertTrue(rv != -1, "Error installing flow mod")
Ken Chiangfb593e72012-03-28 17:19:13 -0700287 fs_logger.info("Inserting flow2")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700288 rv = self.controller.message_send(flow_mod_msg2)
289 self.assertTrue(rv != -1, "Error installing flow mod")
Dan Talayco89d57342010-06-07 16:24:59 -0700290 do_barrier(self.controller)
291
Ken Chiang620bdcc2012-03-23 12:52:07 -0700292 num_pkt1s = random.randint(10,30)
Ken Chiangfb593e72012-03-28 17:19:13 -0700293 fs_logger.info("Sending " + str(num_pkt1s) + " pkt1s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700294 num_pkt2s = random.randint(10,30)
Ken Chiangfb593e72012-03-28 17:19:13 -0700295 fs_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700296 for i in range(0,num_pkt1s):
297 self.sendPacket(pkt1, ingress_port, egress_port1, test_timeout)
298 for i in range(0,num_pkt2s):
299 self.sendPacket(pkt2, ingress_port, egress_port2, test_timeout)
300
301 match1 = parse.packet_to_flow_match(pkt1)
302 self.verifyStats(match1, ofp.OFPP_NONE, test_timeout, num_pkt1s)
303 match2 = parse.packet_to_flow_match(pkt2)
304 self.verifyStats(match2, ofp.OFPP_NONE, test_timeout, num_pkt2s)
305 match1.wildcards |= ofp.OFPFW_DL_SRC
306 self.verifyStats(match1, ofp.OFPP_NONE, test_timeout,
307 num_pkt1s+num_pkt2s)
308 # TODO: sweep through the wildcards to verify matching?
309
310
311class AggregateStats(basic.SimpleDataPlane):
312 """
313 Verify aggregate flow stats are properly retrieved.
314
315 Generate two packets
316 Generate and install two matching flows
317 Send an aggregate stats request
318 Verify that aggregate stats are correct
319 Also verify out_port filtering
320 """
321
322 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
323 match = parse.packet_to_flow_match(pkt)
324 match.wildcards &= ~ofp.OFPFW_IN_PORT
325 self.assertTrue(match is not None,
326 "Could not generate flow match from pkt")
327 match.in_port = ingress_port
328
329 flow_mod_msg = message.flow_mod()
330 flow_mod_msg.match = match
331 flow_mod_msg.cookie = random.randint(0,9007199254740992)
332 flow_mod_msg.buffer_id = 0xffffffff
333 flow_mod_msg.idle_timeout = 0
334 flow_mod_msg.hard_timeout = 0
335 act = action.action_output()
336 act.port = egress_port
337 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
338
Ken Chiangfb593e72012-03-28 17:19:13 -0700339 fs_logger.info("Ingress " + str(ingress_port) +
Ken Chiang620bdcc2012-03-23 12:52:07 -0700340 " to egress " + str(egress_port))
341
342 return flow_mod_msg
343
344 def sendPacket(self, pkt, ingress_port, egress_port, test_timeout):
Ken Chiangfb593e72012-03-28 17:19:13 -0700345 fs_logger.info("Sending packet to dp port " +
Ken Chiang620bdcc2012-03-23 12:52:07 -0700346 str(ingress_port))
347 self.dataplane.send(ingress_port, str(pkt))
Dan Talaycoaff26c82012-03-25 15:06:26 -0700348 (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=
349 test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700350 self.assertTrue(rcv_pkt is not None, "Did not receive packet")
Ken Chiangfb593e72012-03-28 17:19:13 -0700351 fs_logger.debug("Packet len " + str(len(pkt)) + " in on " +
Ken Chiang620bdcc2012-03-23 12:52:07 -0700352 str(rcv_port))
353 self.assertEqual(rcv_port, egress_port, "Unexpected receive port")
354 for j in range(0,test_timeout):
355 if str(pkt) == str(rcv_pkt):
356 break
357 sleep(1)
358 self.assertTrue(j < test_timeout,
359 'Timeout sending packets for flow stats test')
360
361 def verifyAggFlowStats(self, match, out_port, test_timeout,
362 flow_count, packet_count):
363 stat_req = message.aggregate_stats_request()
364 stat_req.match = match
365 stat_req.table_id = 0xff
366 stat_req.out_port = out_port
367
368 all_packets_received = 0
369 for i in range(0,test_timeout):
Ken Chiangfb593e72012-03-28 17:19:13 -0700370 fs_logger.info("Sending stats request")
371 response, pkt = self.controller.transact(stat_req,
372 timeout=test_timeout)
373 self.assertTrue(response is not None,
374 "No response to stats request")
Dan Talaycoaff26c82012-03-25 15:06:26 -0700375 self.assertTrue(len(response.stats) == 1,
376 "Did not receive flow stats reply")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700377 for obj in response.stats:
378 self.assertTrue(obj.flow_count == flow_count,
379 "Flow count " + str(obj.flow_count) +
380 " does not match expected " + str(flow_count))
Ken Chiangfb593e72012-03-28 17:19:13 -0700381 fs_logger.info("Received " + str(obj.packet_count) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700382 if obj.packet_count == packet_count:
383 all_packets_received = 1
384
385 if all_packets_received:
386 break
387 sleep(1)
388
389 self.assertTrue(all_packets_received,
390 "Packet count does not match number sent")
391
392 def runTest(self):
Ken Chiangfb593e72012-03-28 17:19:13 -0700393 global fs_port_map
Ken Chiang620bdcc2012-03-23 12:52:07 -0700394
395 # TODO: set from command-line parameter
396 test_timeout = 60
397
Ken Chiangfb593e72012-03-28 17:19:13 -0700398 of_ports = fs_port_map.keys()
Ken Chiang620bdcc2012-03-23 12:52:07 -0700399 of_ports.sort()
400 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
401 ingress_port = of_ports[0];
402 egress_port1 = of_ports[1];
403 egress_port2 = of_ports[2];
404
Ken Chiangfb593e72012-03-28 17:19:13 -0700405 rc = delete_all_flows(self.controller, fs_logger)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700406 self.assertEqual(rc, 0, "Failed to delete all flows")
407
408 pkt1 = simple_tcp_packet()
409 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
410
411 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
412 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
413
Ken Chiangfb593e72012-03-28 17:19:13 -0700414 fs_logger.info("Inserting flow1")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700415 rv = self.controller.message_send(flow_mod_msg1)
416 self.assertTrue(rv != -1, "Error installing flow mod")
Ken Chiangfb593e72012-03-28 17:19:13 -0700417 fs_logger.info("Inserting flow2")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700418 rv = self.controller.message_send(flow_mod_msg2)
419 self.assertTrue(rv != -1, "Error installing flow mod")
420 do_barrier(self.controller)
421
422 num_pkt1s = random.randint(10,30)
Ken Chiangfb593e72012-03-28 17:19:13 -0700423 fs_logger.info("Sending " + str(num_pkt1s) + " pkt1s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700424 num_pkt2s = random.randint(10,30)
Ken Chiangfb593e72012-03-28 17:19:13 -0700425 fs_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700426 for i in range(0,num_pkt1s):
427 self.sendPacket(pkt1, ingress_port, egress_port1, test_timeout)
428 for i in range(0,num_pkt2s):
429 self.sendPacket(pkt2, ingress_port, egress_port2, test_timeout)
430
431 # loop on flow stats request until timeout
432 match = parse.packet_to_flow_match(pkt1)
433 match.wildcards |= ofp.OFPFW_DL_SRC
434 self.verifyAggFlowStats(match, ofp.OFPP_NONE, test_timeout,
435 2, num_pkt1s+num_pkt2s)
436
437 # out_port filter for egress_port1
438 self.verifyAggFlowStats(match, egress_port1, test_timeout,
439 1, num_pkt1s)
440
441 # out_port filter for egress_port1
442 self.verifyAggFlowStats(match, egress_port2, test_timeout,
443 1, num_pkt2s)