blob: 38560bc0d9458de6b1a4472144ac8ef05d8229ee [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
Ed Swierk89f78352012-03-29 12:32:32 -070052 basic.test_set_init(config)
53
Ken Chiangfb593e72012-03-28 17:19:13 -070054 global fs_port_map
55 global fs_logger
56 global fs_config
Dan Talayco89d57342010-06-07 16:24:59 -070057
Ken Chiangfb593e72012-03-28 17:19:13 -070058 fs_logger = logging.getLogger("flow_stats")
59 fs_logger.info("Initializing test set")
60 fs_port_map = config["port_map"]
61 fs_config = config
Dan Talayco89d57342010-06-07 16:24:59 -070062
Ken Chiangaa5bc062012-03-31 14:03:28 -070063def sendPacket(obj, pkt, ingress_port, egress_port, test_timeout):
64
65 fs_logger.info("Sending packet to dp port " + str(ingress_port) +
66 ", expecting output on " + str(egress_port))
67 obj.dataplane.send(ingress_port, str(pkt))
68
69 exp_pkt_arg = None
70 exp_port = None
71 if fs_config["relax"]:
72 exp_pkt_arg = pkt
73 exp_port = egress_port
74
75 (rcv_port, rcv_pkt, pkt_time) = obj.dataplane.poll(timeout=1,
76 port_number=exp_port,
77 exp_pkt=exp_pkt_arg)
78 obj.assertTrue(rcv_pkt is not None,
79 "Packet not received on port " + str(egress_port))
80 fs_logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
81 str(rcv_port))
82 obj.assertEqual(rcv_port, egress_port,
83 "Packet received on port " + str(rcv_port) +
84 ", expected port " + str(egress_port))
85 obj.assertEqual(str(pkt), str(rcv_pkt),
86 'Response packet does not match send packet')
87
Ken Chiang620bdcc2012-03-23 12:52:07 -070088class SingleFlowStats(basic.SimpleDataPlane):
Dan Talayco89d57342010-06-07 16:24:59 -070089 """
90 Verify flow stats are properly retrieved.
91
92 Generate a packet
Ken Chiang620bdcc2012-03-23 12:52:07 -070093 Generate and install a matching flow
94 Send the packet
95 Send a flow stats request to match the flow and retrieve stats
96 Verify that the packet counter has incremented
Dan Talayco89d57342010-06-07 16:24:59 -070097 """
Ken Chiang620bdcc2012-03-23 12:52:07 -070098
99 def verifyStats(self, match, out_port, test_timeout, packet_count):
100 stat_req = message.flow_stats_request()
101 stat_req.match = match
102 stat_req.table_id = 0xff
103 stat_req.out_port = out_port
104
105 all_packets_received = 0
106 for i in range(0,test_timeout):
Ken Chiangfb593e72012-03-28 17:19:13 -0700107 fs_logger.info("Sending stats request")
108 response, pkt = self.controller.transact(stat_req,
109 timeout=test_timeout)
110 self.assertTrue(response is not None,
111 "No response to stats request")
112 self.assertTrue(len(response.stats) == 1,
113 "Did not receive flow stats reply")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700114 for obj in response.stats:
115 # TODO: pad1 and pad2 fields may be nonzero, is this a bug?
116 # for now, just clear them so the assert is simpler
117 #obj.match.pad1 = 0
118 #obj.match.pad2 = [0, 0]
119 #self.assertEqual(match, obj.match,
120 # "Matches do not match")
Ken Chiangfb593e72012-03-28 17:19:13 -0700121 fs_logger.info("Received " + str(obj.packet_count) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700122 if obj.packet_count == packet_count:
123 all_packets_received = 1
124
125 if all_packets_received:
126 break
127 sleep(1)
128
129 self.assertTrue(all_packets_received,
130 "Packet count does not match number sent")
131
Dan Talayco89d57342010-06-07 16:24:59 -0700132 def runTest(self):
Ken Chiangfb593e72012-03-28 17:19:13 -0700133 global fs_port_map
Dan Talayco89d57342010-06-07 16:24:59 -0700134
Ken Chiang620bdcc2012-03-23 12:52:07 -0700135 # TODO: set from command-line parameter
136 test_timeout = 60
137
Ken Chiangfb593e72012-03-28 17:19:13 -0700138 of_ports = fs_port_map.keys()
Dan Talayco89d57342010-06-07 16:24:59 -0700139 of_ports.sort()
140 self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
141
Ken Chiangfb593e72012-03-28 17:19:13 -0700142 rc = delete_all_flows(self.controller, fs_logger)
Dan Talayco89d57342010-06-07 16:24:59 -0700143 self.assertEqual(rc, 0, "Failed to delete all flows")
144
Ken Chiang620bdcc2012-03-23 12:52:07 -0700145 # build packet
Dan Talayco89d57342010-06-07 16:24:59 -0700146 pkt = simple_tcp_packet()
147 match = parse.packet_to_flow_match(pkt)
148 match.wildcards &= ~ofp.OFPFW_IN_PORT
149 self.assertTrue(match is not None,
150 "Could not generate flow match from pkt")
151 act = action.action_output()
152
Ken Chiang620bdcc2012-03-23 12:52:07 -0700153 # build flow
Dan Talayco39bf6912010-07-08 14:17:52 -0700154 ingress_port = of_ports[0];
155 egress_port = of_ports[1];
Ken Chiangfb593e72012-03-28 17:19:13 -0700156 fs_logger.info("Ingress " + str(ingress_port) +
Dan Talayco89d57342010-06-07 16:24:59 -0700157 " to egress " + str(egress_port))
Ken Chiang620bdcc2012-03-23 12:52:07 -0700158 match.in_port = ingress_port
159 flow_mod_msg = message.flow_mod()
160 flow_mod_msg.match = match
161 flow_mod_msg.cookie = random.randint(0,9007199254740992)
162 flow_mod_msg.buffer_id = 0xffffffff
163 flow_mod_msg.idle_timeout = 0
164 flow_mod_msg.hard_timeout = 0
165 act.port = egress_port
166 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
167
168 # send flow
Ken Chiangfb593e72012-03-28 17:19:13 -0700169 fs_logger.info("Inserting flow")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700170 rv = self.controller.message_send(flow_mod_msg)
171 self.assertTrue(rv != -1, "Error installing flow mod")
172 do_barrier(self.controller)
173
174 # no packets sent, so zero packet count
175 self.verifyStats(match, ofp.OFPP_NONE, test_timeout, 0)
176
177 # send packet N times
178 num_sends = random.randint(10,20)
Ken Chiangfb593e72012-03-28 17:19:13 -0700179 fs_logger.info("Sending " + str(num_sends) + " test packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700180 for i in range(0,num_sends):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700181 sendPacket(self, pkt, ingress_port, egress_port,
182 test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700183
184 self.verifyStats(match, ofp.OFPP_NONE, test_timeout, num_sends)
185 self.verifyStats(match, egress_port, test_timeout, num_sends)
186 for wc in WILDCARD_VALUES:
187 match.wildcards = wc
188 self.verifyStats(match, egress_port, test_timeout, num_sends)
189
190
191class TwoFlowStats(basic.SimpleDataPlane):
192 """
193 Verify flow stats are properly retrieved.
194
195 Generate two packets and install two matching flows
196 Send some number of packets
197 Send a flow stats request to match the flows and retrieve stats
198 Verify that the packet counter has incremented
199
200 TODO: add a third flow, and then configure the match to exclude
201 that flow?
202 """
203
204 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
205 match = parse.packet_to_flow_match(pkt)
206 match.wildcards &= ~ofp.OFPFW_IN_PORT
207 self.assertTrue(match is not None,
208 "Could not generate flow match from pkt")
Dan Talayco89d57342010-06-07 16:24:59 -0700209 match.in_port = ingress_port
210
211 flow_mod_msg = message.flow_mod()
212 flow_mod_msg.match = match
213 flow_mod_msg.cookie = random.randint(0,9007199254740992)
214 flow_mod_msg.buffer_id = 0xffffffff
Ken Chiang620bdcc2012-03-23 12:52:07 -0700215 flow_mod_msg.idle_timeout = 0
216 flow_mod_msg.hard_timeout = 0
217 act = action.action_output()
Dan Talayco89d57342010-06-07 16:24:59 -0700218 act.port = egress_port
219 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
Dan Talayco89d57342010-06-07 16:24:59 -0700220
Ken Chiangfb593e72012-03-28 17:19:13 -0700221 fs_logger.info("Ingress " + str(ingress_port) +
Ken Chiang620bdcc2012-03-23 12:52:07 -0700222 " to egress " + str(egress_port))
Dan Talayco89d57342010-06-07 16:24:59 -0700223
Ken Chiang620bdcc2012-03-23 12:52:07 -0700224 return flow_mod_msg
Dan Talayco89d57342010-06-07 16:24:59 -0700225
Ken Chiangaa5bc062012-03-31 14:03:28 -0700226 def sumStatsReplyCounts(self, response):
227 total_packets = 0
228 for obj in response.stats:
229 # TODO: pad1 and pad2 fields may be nonzero, is this a bug?
230 # for now, just clear them so the assert is simpler
231 #obj.match.pad1 = 0
232 #obj.match.pad2 = [0, 0]
233 #self.assertEqual(match, obj.match,
234 # "Matches do not match")
235 fs_logger.info("Received " + str(obj.packet_count)
236 + " packets")
237 total_packets += obj.packet_count
238 return total_packets
Ken Chiang620bdcc2012-03-23 12:52:07 -0700239
240 def verifyStats(self, match, out_port, test_timeout, packet_count):
241 stat_req = message.flow_stats_request()
242 stat_req.match = match
243 stat_req.table_id = 0xff
244 stat_req.out_port = out_port
245
246 all_packets_received = 0
247 for i in range(0,test_timeout):
Ken Chiangfb593e72012-03-28 17:19:13 -0700248 fs_logger.info("Sending stats request")
Ken Chiangaa5bc062012-03-31 14:03:28 -0700249 # TODO: move REPLY_MORE handling to controller.transact?
Ken Chiangfb593e72012-03-28 17:19:13 -0700250 response, pkt = self.controller.transact(stat_req,
251 timeout=test_timeout)
252 self.assertTrue(response is not None,
253 "No response to stats request")
Ken Chiangaa5bc062012-03-31 14:03:28 -0700254 total_packets = self.sumStatsReplyCounts(response)
255
256 while response.flags == ofp.OFPSF_REPLY_MORE:
257 response, pkt = self.controller.poll(exp_msg=
258 ofp.OFPT_STATS_REPLY,
259 timeout=test_timeout)
260 total_packets += self.sumStatsReplyCounts(response)
261
Ken Chiang620bdcc2012-03-23 12:52:07 -0700262 if total_packets == packet_count:
263 all_packets_received = 1
264 break
265 sleep(1)
266
267 self.assertTrue(all_packets_received,
Ken Chiangaa5bc062012-03-31 14:03:28 -0700268 "Total stats packet count " + str(total_packets) +
269 " does not match number sent " + str(packet_count))
Ken Chiang620bdcc2012-03-23 12:52:07 -0700270
271 def runTest(self):
Ken Chiangfb593e72012-03-28 17:19:13 -0700272 global fs_port_map
Ken Chiang620bdcc2012-03-23 12:52:07 -0700273
274 # TODO: set from command-line parameter
275 test_timeout = 60
276
Ken Chiangfb593e72012-03-28 17:19:13 -0700277 of_ports = fs_port_map.keys()
Ken Chiang620bdcc2012-03-23 12:52:07 -0700278 of_ports.sort()
279 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
280 ingress_port = of_ports[0];
281 egress_port1 = of_ports[1];
282 egress_port2 = of_ports[2];
283
Ken Chiangfb593e72012-03-28 17:19:13 -0700284 rc = delete_all_flows(self.controller, fs_logger)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700285 self.assertEqual(rc, 0, "Failed to delete all flows")
286
287 pkt1 = simple_tcp_packet()
288 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
289
290 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
291 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
292
Ken Chiangfb593e72012-03-28 17:19:13 -0700293 fs_logger.info("Inserting flow1")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700294 rv = self.controller.message_send(flow_mod_msg1)
295 self.assertTrue(rv != -1, "Error installing flow mod")
Ken Chiangfb593e72012-03-28 17:19:13 -0700296 fs_logger.info("Inserting flow2")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700297 rv = self.controller.message_send(flow_mod_msg2)
298 self.assertTrue(rv != -1, "Error installing flow mod")
Dan Talayco89d57342010-06-07 16:24:59 -0700299 do_barrier(self.controller)
300
Ken Chiang620bdcc2012-03-23 12:52:07 -0700301 num_pkt1s = random.randint(10,30)
Ken Chiangfb593e72012-03-28 17:19:13 -0700302 fs_logger.info("Sending " + str(num_pkt1s) + " pkt1s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700303 num_pkt2s = random.randint(10,30)
Ken Chiangfb593e72012-03-28 17:19:13 -0700304 fs_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700305 for i in range(0,num_pkt1s):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700306 sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700307 for i in range(0,num_pkt2s):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700308 sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700309
310 match1 = parse.packet_to_flow_match(pkt1)
Ken Chiangaa5bc062012-03-31 14:03:28 -0700311 fs_logger.info("Verifying flow1's " + str(num_pkt1s) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700312 self.verifyStats(match1, ofp.OFPP_NONE, test_timeout, num_pkt1s)
313 match2 = parse.packet_to_flow_match(pkt2)
Ken Chiangaa5bc062012-03-31 14:03:28 -0700314 fs_logger.info("Verifying flow2's " + str(num_pkt2s) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700315 self.verifyStats(match2, ofp.OFPP_NONE, test_timeout, num_pkt2s)
316 match1.wildcards |= ofp.OFPFW_DL_SRC
Ken Chiangaa5bc062012-03-31 14:03:28 -0700317 fs_logger.info("Verifying combined " + str(num_pkt1s+num_pkt2s) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700318 self.verifyStats(match1, ofp.OFPP_NONE, test_timeout,
319 num_pkt1s+num_pkt2s)
320 # TODO: sweep through the wildcards to verify matching?
321
322
323class AggregateStats(basic.SimpleDataPlane):
324 """
325 Verify aggregate flow stats are properly retrieved.
326
327 Generate two packets
328 Generate and install two matching flows
329 Send an aggregate stats request
330 Verify that aggregate stats are correct
331 Also verify out_port filtering
332 """
333
334 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
335 match = parse.packet_to_flow_match(pkt)
336 match.wildcards &= ~ofp.OFPFW_IN_PORT
337 self.assertTrue(match is not None,
338 "Could not generate flow match from pkt")
339 match.in_port = ingress_port
340
341 flow_mod_msg = message.flow_mod()
342 flow_mod_msg.match = match
343 flow_mod_msg.cookie = random.randint(0,9007199254740992)
344 flow_mod_msg.buffer_id = 0xffffffff
345 flow_mod_msg.idle_timeout = 0
346 flow_mod_msg.hard_timeout = 0
347 act = action.action_output()
348 act.port = egress_port
349 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
350
Ken Chiangfb593e72012-03-28 17:19:13 -0700351 fs_logger.info("Ingress " + str(ingress_port) +
Ken Chiang620bdcc2012-03-23 12:52:07 -0700352 " to egress " + str(egress_port))
353
354 return flow_mod_msg
355
Ken Chiang620bdcc2012-03-23 12:52:07 -0700356 def verifyAggFlowStats(self, match, out_port, test_timeout,
357 flow_count, packet_count):
358 stat_req = message.aggregate_stats_request()
359 stat_req.match = match
360 stat_req.table_id = 0xff
361 stat_req.out_port = out_port
362
363 all_packets_received = 0
364 for i in range(0,test_timeout):
Ken Chiangfb593e72012-03-28 17:19:13 -0700365 fs_logger.info("Sending stats request")
366 response, pkt = self.controller.transact(stat_req,
367 timeout=test_timeout)
368 self.assertTrue(response is not None,
369 "No response to stats request")
Dan Talaycoaff26c82012-03-25 15:06:26 -0700370 self.assertTrue(len(response.stats) == 1,
371 "Did not receive flow stats reply")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700372 for obj in response.stats:
373 self.assertTrue(obj.flow_count == flow_count,
374 "Flow count " + str(obj.flow_count) +
375 " does not match expected " + str(flow_count))
Ken Chiangfb593e72012-03-28 17:19:13 -0700376 fs_logger.info("Received " + str(obj.packet_count) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700377 if obj.packet_count == packet_count:
378 all_packets_received = 1
379
380 if all_packets_received:
381 break
382 sleep(1)
383
384 self.assertTrue(all_packets_received,
385 "Packet count does not match number sent")
386
387 def runTest(self):
Ken Chiangfb593e72012-03-28 17:19:13 -0700388 global fs_port_map
Ken Chiang620bdcc2012-03-23 12:52:07 -0700389
390 # TODO: set from command-line parameter
391 test_timeout = 60
392
Ken Chiangfb593e72012-03-28 17:19:13 -0700393 of_ports = fs_port_map.keys()
Ken Chiang620bdcc2012-03-23 12:52:07 -0700394 of_ports.sort()
395 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
396 ingress_port = of_ports[0];
397 egress_port1 = of_ports[1];
398 egress_port2 = of_ports[2];
399
Ken Chiangfb593e72012-03-28 17:19:13 -0700400 rc = delete_all_flows(self.controller, fs_logger)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700401 self.assertEqual(rc, 0, "Failed to delete all flows")
402
403 pkt1 = simple_tcp_packet()
404 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
405
406 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
407 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
408
Ken Chiangfb593e72012-03-28 17:19:13 -0700409 fs_logger.info("Inserting flow1")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700410 rv = self.controller.message_send(flow_mod_msg1)
411 self.assertTrue(rv != -1, "Error installing flow mod")
Ken Chiangfb593e72012-03-28 17:19:13 -0700412 fs_logger.info("Inserting flow2")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700413 rv = self.controller.message_send(flow_mod_msg2)
414 self.assertTrue(rv != -1, "Error installing flow mod")
415 do_barrier(self.controller)
416
417 num_pkt1s = random.randint(10,30)
Ken Chiangfb593e72012-03-28 17:19:13 -0700418 fs_logger.info("Sending " + str(num_pkt1s) + " pkt1s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700419 num_pkt2s = random.randint(10,30)
Ken Chiangfb593e72012-03-28 17:19:13 -0700420 fs_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700421 for i in range(0,num_pkt1s):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700422 sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700423 for i in range(0,num_pkt2s):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700424 sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700425
426 # loop on flow stats request until timeout
427 match = parse.packet_to_flow_match(pkt1)
428 match.wildcards |= ofp.OFPFW_DL_SRC
429 self.verifyAggFlowStats(match, ofp.OFPP_NONE, test_timeout,
430 2, num_pkt1s+num_pkt2s)
431
432 # out_port filter for egress_port1
433 self.verifyAggFlowStats(match, egress_port1, test_timeout,
434 1, num_pkt1s)
435
436 # out_port filter for egress_port1
437 self.verifyAggFlowStats(match, egress_port2, test_timeout,
438 1, num_pkt2s)