blob: e5976c3f05788c23ecca8cc7b659260edcebbbb6 [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 Chiangaa5bc062012-03-31 14:03:28 -070061def sendPacket(obj, pkt, ingress_port, egress_port, test_timeout):
62
63 fs_logger.info("Sending packet to dp port " + str(ingress_port) +
64 ", expecting output on " + str(egress_port))
65 obj.dataplane.send(ingress_port, str(pkt))
66
67 exp_pkt_arg = None
68 exp_port = None
69 if fs_config["relax"]:
70 exp_pkt_arg = pkt
71 exp_port = egress_port
72
73 (rcv_port, rcv_pkt, pkt_time) = obj.dataplane.poll(timeout=1,
74 port_number=exp_port,
75 exp_pkt=exp_pkt_arg)
76 obj.assertTrue(rcv_pkt is not None,
77 "Packet not received on port " + str(egress_port))
78 fs_logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
79 str(rcv_port))
80 obj.assertEqual(rcv_port, egress_port,
81 "Packet received on port " + str(rcv_port) +
82 ", expected port " + str(egress_port))
83 obj.assertEqual(str(pkt), str(rcv_pkt),
84 'Response packet does not match send packet')
85
Ken Chiang620bdcc2012-03-23 12:52:07 -070086class SingleFlowStats(basic.SimpleDataPlane):
Dan Talayco89d57342010-06-07 16:24:59 -070087 """
88 Verify flow stats are properly retrieved.
89
90 Generate a packet
Ken Chiang620bdcc2012-03-23 12:52:07 -070091 Generate and install a matching flow
92 Send the packet
93 Send a flow stats request to match the flow and retrieve stats
94 Verify that the packet counter has incremented
Dan Talayco89d57342010-06-07 16:24:59 -070095 """
Ken Chiang620bdcc2012-03-23 12:52:07 -070096
97 def verifyStats(self, match, out_port, test_timeout, packet_count):
98 stat_req = message.flow_stats_request()
99 stat_req.match = match
100 stat_req.table_id = 0xff
101 stat_req.out_port = out_port
102
103 all_packets_received = 0
104 for i in range(0,test_timeout):
Ken Chiangfb593e72012-03-28 17:19:13 -0700105 fs_logger.info("Sending stats request")
106 response, pkt = self.controller.transact(stat_req,
107 timeout=test_timeout)
108 self.assertTrue(response is not None,
109 "No response to stats request")
110 self.assertTrue(len(response.stats) == 1,
111 "Did not receive flow stats reply")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700112 for obj in response.stats:
113 # TODO: pad1 and pad2 fields may be nonzero, is this a bug?
114 # for now, just clear them so the assert is simpler
115 #obj.match.pad1 = 0
116 #obj.match.pad2 = [0, 0]
117 #self.assertEqual(match, obj.match,
118 # "Matches do not match")
Ken Chiangfb593e72012-03-28 17:19:13 -0700119 fs_logger.info("Received " + str(obj.packet_count) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700120 if obj.packet_count == packet_count:
121 all_packets_received = 1
122
123 if all_packets_received:
124 break
125 sleep(1)
126
127 self.assertTrue(all_packets_received,
128 "Packet count does not match number sent")
129
Dan Talayco89d57342010-06-07 16:24:59 -0700130 def runTest(self):
Ken Chiangfb593e72012-03-28 17:19:13 -0700131 global fs_port_map
Dan Talayco89d57342010-06-07 16:24:59 -0700132
Ken Chiang620bdcc2012-03-23 12:52:07 -0700133 # TODO: set from command-line parameter
134 test_timeout = 60
135
Ken Chiangfb593e72012-03-28 17:19:13 -0700136 of_ports = fs_port_map.keys()
Dan Talayco89d57342010-06-07 16:24:59 -0700137 of_ports.sort()
138 self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
139
Ken Chiangfb593e72012-03-28 17:19:13 -0700140 rc = delete_all_flows(self.controller, fs_logger)
Dan Talayco89d57342010-06-07 16:24:59 -0700141 self.assertEqual(rc, 0, "Failed to delete all flows")
142
Ken Chiang620bdcc2012-03-23 12:52:07 -0700143 # build packet
Dan Talayco89d57342010-06-07 16:24:59 -0700144 pkt = simple_tcp_packet()
145 match = parse.packet_to_flow_match(pkt)
146 match.wildcards &= ~ofp.OFPFW_IN_PORT
147 self.assertTrue(match is not None,
148 "Could not generate flow match from pkt")
149 act = action.action_output()
150
Ken Chiang620bdcc2012-03-23 12:52:07 -0700151 # build flow
Dan Talayco39bf6912010-07-08 14:17:52 -0700152 ingress_port = of_ports[0];
153 egress_port = of_ports[1];
Ken Chiangfb593e72012-03-28 17:19:13 -0700154 fs_logger.info("Ingress " + str(ingress_port) +
Dan Talayco89d57342010-06-07 16:24:59 -0700155 " to egress " + str(egress_port))
Ken Chiang620bdcc2012-03-23 12:52:07 -0700156 match.in_port = ingress_port
157 flow_mod_msg = message.flow_mod()
158 flow_mod_msg.match = match
159 flow_mod_msg.cookie = random.randint(0,9007199254740992)
160 flow_mod_msg.buffer_id = 0xffffffff
161 flow_mod_msg.idle_timeout = 0
162 flow_mod_msg.hard_timeout = 0
163 act.port = egress_port
164 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
165
166 # send flow
Ken Chiangfb593e72012-03-28 17:19:13 -0700167 fs_logger.info("Inserting flow")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700168 rv = self.controller.message_send(flow_mod_msg)
169 self.assertTrue(rv != -1, "Error installing flow mod")
170 do_barrier(self.controller)
171
172 # no packets sent, so zero packet count
173 self.verifyStats(match, ofp.OFPP_NONE, test_timeout, 0)
174
175 # send packet N times
176 num_sends = random.randint(10,20)
Ken Chiangfb593e72012-03-28 17:19:13 -0700177 fs_logger.info("Sending " + str(num_sends) + " test packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700178 for i in range(0,num_sends):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700179 sendPacket(self, pkt, ingress_port, egress_port,
180 test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700181
182 self.verifyStats(match, ofp.OFPP_NONE, test_timeout, num_sends)
183 self.verifyStats(match, egress_port, test_timeout, num_sends)
184 for wc in WILDCARD_VALUES:
185 match.wildcards = wc
186 self.verifyStats(match, egress_port, test_timeout, num_sends)
187
188
189class TwoFlowStats(basic.SimpleDataPlane):
190 """
191 Verify flow stats are properly retrieved.
192
193 Generate two packets and install two matching flows
194 Send some number of packets
195 Send a flow stats request to match the flows and retrieve stats
196 Verify that the packet counter has incremented
197
198 TODO: add a third flow, and then configure the match to exclude
199 that flow?
200 """
201
202 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
203 match = parse.packet_to_flow_match(pkt)
204 match.wildcards &= ~ofp.OFPFW_IN_PORT
205 self.assertTrue(match is not None,
206 "Could not generate flow match from pkt")
Dan Talayco89d57342010-06-07 16:24:59 -0700207 match.in_port = ingress_port
208
209 flow_mod_msg = message.flow_mod()
210 flow_mod_msg.match = match
211 flow_mod_msg.cookie = random.randint(0,9007199254740992)
212 flow_mod_msg.buffer_id = 0xffffffff
Ken Chiang620bdcc2012-03-23 12:52:07 -0700213 flow_mod_msg.idle_timeout = 0
214 flow_mod_msg.hard_timeout = 0
215 act = action.action_output()
Dan Talayco89d57342010-06-07 16:24:59 -0700216 act.port = egress_port
217 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
Dan Talayco89d57342010-06-07 16:24:59 -0700218
Ken Chiangfb593e72012-03-28 17:19:13 -0700219 fs_logger.info("Ingress " + str(ingress_port) +
Ken Chiang620bdcc2012-03-23 12:52:07 -0700220 " to egress " + str(egress_port))
Dan Talayco89d57342010-06-07 16:24:59 -0700221
Ken Chiang620bdcc2012-03-23 12:52:07 -0700222 return flow_mod_msg
Dan Talayco89d57342010-06-07 16:24:59 -0700223
Ken Chiangaa5bc062012-03-31 14:03:28 -0700224 def sumStatsReplyCounts(self, response):
225 total_packets = 0
226 for obj in response.stats:
227 # TODO: pad1 and pad2 fields may be nonzero, is this a bug?
228 # for now, just clear them so the assert is simpler
229 #obj.match.pad1 = 0
230 #obj.match.pad2 = [0, 0]
231 #self.assertEqual(match, obj.match,
232 # "Matches do not match")
233 fs_logger.info("Received " + str(obj.packet_count)
234 + " packets")
235 total_packets += obj.packet_count
236 return total_packets
Ken Chiang620bdcc2012-03-23 12:52:07 -0700237
238 def verifyStats(self, match, out_port, test_timeout, packet_count):
239 stat_req = message.flow_stats_request()
240 stat_req.match = match
241 stat_req.table_id = 0xff
242 stat_req.out_port = out_port
243
244 all_packets_received = 0
245 for i in range(0,test_timeout):
Ken Chiangfb593e72012-03-28 17:19:13 -0700246 fs_logger.info("Sending stats request")
Ken Chiangaa5bc062012-03-31 14:03:28 -0700247 # TODO: move REPLY_MORE handling to controller.transact?
Ken Chiangfb593e72012-03-28 17:19:13 -0700248 response, pkt = self.controller.transact(stat_req,
249 timeout=test_timeout)
250 self.assertTrue(response is not None,
251 "No response to stats request")
Ken Chiangaa5bc062012-03-31 14:03:28 -0700252 total_packets = self.sumStatsReplyCounts(response)
253
254 while response.flags == ofp.OFPSF_REPLY_MORE:
255 response, pkt = self.controller.poll(exp_msg=
256 ofp.OFPT_STATS_REPLY,
257 timeout=test_timeout)
258 total_packets += self.sumStatsReplyCounts(response)
259
Ken Chiang620bdcc2012-03-23 12:52:07 -0700260 if total_packets == packet_count:
261 all_packets_received = 1
262 break
263 sleep(1)
264
265 self.assertTrue(all_packets_received,
Ken Chiangaa5bc062012-03-31 14:03:28 -0700266 "Total stats packet count " + str(total_packets) +
267 " does not match number sent " + str(packet_count))
Ken Chiang620bdcc2012-03-23 12:52:07 -0700268
269 def runTest(self):
Ken Chiangfb593e72012-03-28 17:19:13 -0700270 global fs_port_map
Ken Chiang620bdcc2012-03-23 12:52:07 -0700271
272 # TODO: set from command-line parameter
273 test_timeout = 60
274
Ken Chiangfb593e72012-03-28 17:19:13 -0700275 of_ports = fs_port_map.keys()
Ken Chiang620bdcc2012-03-23 12:52:07 -0700276 of_ports.sort()
277 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
278 ingress_port = of_ports[0];
279 egress_port1 = of_ports[1];
280 egress_port2 = of_ports[2];
281
Ken Chiangfb593e72012-03-28 17:19:13 -0700282 rc = delete_all_flows(self.controller, fs_logger)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700283 self.assertEqual(rc, 0, "Failed to delete all flows")
284
285 pkt1 = simple_tcp_packet()
286 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
287
288 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
289 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
290
Ken Chiangfb593e72012-03-28 17:19:13 -0700291 fs_logger.info("Inserting flow1")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700292 rv = self.controller.message_send(flow_mod_msg1)
293 self.assertTrue(rv != -1, "Error installing flow mod")
Ken Chiangfb593e72012-03-28 17:19:13 -0700294 fs_logger.info("Inserting flow2")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700295 rv = self.controller.message_send(flow_mod_msg2)
296 self.assertTrue(rv != -1, "Error installing flow mod")
Dan Talayco89d57342010-06-07 16:24:59 -0700297 do_barrier(self.controller)
298
Ken Chiang620bdcc2012-03-23 12:52:07 -0700299 num_pkt1s = random.randint(10,30)
Ken Chiangfb593e72012-03-28 17:19:13 -0700300 fs_logger.info("Sending " + str(num_pkt1s) + " pkt1s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700301 num_pkt2s = random.randint(10,30)
Ken Chiangfb593e72012-03-28 17:19:13 -0700302 fs_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700303 for i in range(0,num_pkt1s):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700304 sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700305 for i in range(0,num_pkt2s):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700306 sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700307
308 match1 = parse.packet_to_flow_match(pkt1)
Ken Chiangaa5bc062012-03-31 14:03:28 -0700309 fs_logger.info("Verifying flow1's " + str(num_pkt1s) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700310 self.verifyStats(match1, ofp.OFPP_NONE, test_timeout, num_pkt1s)
311 match2 = parse.packet_to_flow_match(pkt2)
Ken Chiangaa5bc062012-03-31 14:03:28 -0700312 fs_logger.info("Verifying flow2's " + str(num_pkt2s) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700313 self.verifyStats(match2, ofp.OFPP_NONE, test_timeout, num_pkt2s)
314 match1.wildcards |= ofp.OFPFW_DL_SRC
Ken Chiangaa5bc062012-03-31 14:03:28 -0700315 fs_logger.info("Verifying combined " + str(num_pkt1s+num_pkt2s) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700316 self.verifyStats(match1, ofp.OFPP_NONE, test_timeout,
317 num_pkt1s+num_pkt2s)
318 # TODO: sweep through the wildcards to verify matching?
319
320
321class AggregateStats(basic.SimpleDataPlane):
322 """
323 Verify aggregate flow stats are properly retrieved.
324
325 Generate two packets
326 Generate and install two matching flows
327 Send an aggregate stats request
328 Verify that aggregate stats are correct
329 Also verify out_port filtering
330 """
331
332 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
333 match = parse.packet_to_flow_match(pkt)
334 match.wildcards &= ~ofp.OFPFW_IN_PORT
335 self.assertTrue(match is not None,
336 "Could not generate flow match from pkt")
337 match.in_port = ingress_port
338
339 flow_mod_msg = message.flow_mod()
340 flow_mod_msg.match = match
341 flow_mod_msg.cookie = random.randint(0,9007199254740992)
342 flow_mod_msg.buffer_id = 0xffffffff
343 flow_mod_msg.idle_timeout = 0
344 flow_mod_msg.hard_timeout = 0
345 act = action.action_output()
346 act.port = egress_port
347 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
348
Ken Chiangfb593e72012-03-28 17:19:13 -0700349 fs_logger.info("Ingress " + str(ingress_port) +
Ken Chiang620bdcc2012-03-23 12:52:07 -0700350 " to egress " + str(egress_port))
351
352 return flow_mod_msg
353
Ken Chiang620bdcc2012-03-23 12:52:07 -0700354 def verifyAggFlowStats(self, match, out_port, test_timeout,
355 flow_count, packet_count):
356 stat_req = message.aggregate_stats_request()
357 stat_req.match = match
358 stat_req.table_id = 0xff
359 stat_req.out_port = out_port
360
361 all_packets_received = 0
362 for i in range(0,test_timeout):
Ken Chiangfb593e72012-03-28 17:19:13 -0700363 fs_logger.info("Sending stats request")
364 response, pkt = self.controller.transact(stat_req,
365 timeout=test_timeout)
366 self.assertTrue(response is not None,
367 "No response to stats request")
Dan Talaycoaff26c82012-03-25 15:06:26 -0700368 self.assertTrue(len(response.stats) == 1,
369 "Did not receive flow stats reply")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700370 for obj in response.stats:
371 self.assertTrue(obj.flow_count == flow_count,
372 "Flow count " + str(obj.flow_count) +
373 " does not match expected " + str(flow_count))
Ken Chiangfb593e72012-03-28 17:19:13 -0700374 fs_logger.info("Received " + str(obj.packet_count) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700375 if obj.packet_count == packet_count:
376 all_packets_received = 1
377
378 if all_packets_received:
379 break
380 sleep(1)
381
382 self.assertTrue(all_packets_received,
383 "Packet count does not match number sent")
384
385 def runTest(self):
Ken Chiangfb593e72012-03-28 17:19:13 -0700386 global fs_port_map
Ken Chiang620bdcc2012-03-23 12:52:07 -0700387
388 # TODO: set from command-line parameter
389 test_timeout = 60
390
Ken Chiangfb593e72012-03-28 17:19:13 -0700391 of_ports = fs_port_map.keys()
Ken Chiang620bdcc2012-03-23 12:52:07 -0700392 of_ports.sort()
393 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
394 ingress_port = of_ports[0];
395 egress_port1 = of_ports[1];
396 egress_port2 = of_ports[2];
397
Ken Chiangfb593e72012-03-28 17:19:13 -0700398 rc = delete_all_flows(self.controller, fs_logger)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700399 self.assertEqual(rc, 0, "Failed to delete all flows")
400
401 pkt1 = simple_tcp_packet()
402 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
403
404 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
405 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
406
Ken Chiangfb593e72012-03-28 17:19:13 -0700407 fs_logger.info("Inserting flow1")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700408 rv = self.controller.message_send(flow_mod_msg1)
409 self.assertTrue(rv != -1, "Error installing flow mod")
Ken Chiangfb593e72012-03-28 17:19:13 -0700410 fs_logger.info("Inserting flow2")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700411 rv = self.controller.message_send(flow_mod_msg2)
412 self.assertTrue(rv != -1, "Error installing flow mod")
413 do_barrier(self.controller)
414
415 num_pkt1s = random.randint(10,30)
Ken Chiangfb593e72012-03-28 17:19:13 -0700416 fs_logger.info("Sending " + str(num_pkt1s) + " pkt1s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700417 num_pkt2s = random.randint(10,30)
Ken Chiangfb593e72012-03-28 17:19:13 -0700418 fs_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700419 for i in range(0,num_pkt1s):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700420 sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700421 for i in range(0,num_pkt2s):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700422 sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700423
424 # loop on flow stats request until timeout
425 match = parse.packet_to_flow_match(pkt1)
426 match.wildcards |= ofp.OFPFW_DL_SRC
427 self.verifyAggFlowStats(match, ofp.OFPP_NONE, test_timeout,
428 2, num_pkt1s+num_pkt2s)
429
430 # out_port filter for egress_port1
431 self.verifyAggFlowStats(match, egress_port1, test_timeout,
432 1, num_pkt1s)
433
434 # out_port filter for egress_port1
435 self.verifyAggFlowStats(match, egress_port2, test_timeout,
436 1, num_pkt2s)