blob: 9f71616330cb7e949284883225e6115fa5fa197a [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
Rich Lanec42dbad2012-10-11 10:57:44 -070011import copy
Dan Talayco89d57342010-06-07 16:24:59 -070012
Rich Lane477f4812012-10-04 22:49:00 -070013from oftest import config
Dan Talayco89d57342010-06-07 16:24:59 -070014import oftest.controller as controller
Rich Laned7b0ffa2013-03-08 15:53:42 -080015import ofp
Dan Talayco89d57342010-06-07 16:24:59 -070016import oftest.dataplane as dataplane
Dan Talayco89d57342010-06-07 16:24:59 -070017import oftest.parse as parse
Rich Laneb90a1c42012-10-05 09:16:05 -070018import oftest.base_tests as base_tests
Dan Talayco89d57342010-06-07 16:24:59 -070019
Rich Laneda3b5ad2012-10-03 09:05:32 -070020from oftest.testutils import *
Dan Talayco89d57342010-06-07 16:24:59 -070021from time import sleep
22
Ken Chiang620bdcc2012-03-23 12:52:07 -070023# TODO: ovs has problems with VLAN id?
24WILDCARD_VALUES = [ofp.OFPFW_IN_PORT,
Dan Talayco488fbc52012-04-09 16:30:41 -070025 # (ofp.OFPFW_DL_VLAN | ofp.OFPFW_DL_VLAN_PCP),
Ken Chiang620bdcc2012-03-23 12:52:07 -070026 ofp.OFPFW_DL_SRC,
27 ofp.OFPFW_DL_DST,
Dan Talayco488fbc52012-04-09 16:30:41 -070028 (ofp.OFPFW_DL_TYPE | ofp.OFPFW_NW_SRC_ALL |
29 ofp.OFPFW_NW_DST_ALL | ofp.OFPFW_NW_TOS | ofp.OFPFW_NW_PROTO |
30 ofp.OFPFW_TP_SRC | ofp.OFPFW_TP_DST),
31 (ofp.OFPFW_NW_PROTO | ofp.OFPFW_TP_SRC | ofp.OFPFW_TP_DST),
Ken Chiang620bdcc2012-03-23 12:52:07 -070032 ofp.OFPFW_TP_SRC,
33 ofp.OFPFW_TP_DST,
Dan Talayco488fbc52012-04-09 16:30:41 -070034 ofp.OFPFW_NW_SRC_MASK,
35 ofp.OFPFW_NW_DST_MASK,
Ken Chiang620bdcc2012-03-23 12:52:07 -070036 ofp.OFPFW_DL_VLAN_PCP,
37 ofp.OFPFW_NW_TOS]
38
Ken Chiangaa5bc062012-03-31 14:03:28 -070039def sendPacket(obj, pkt, ingress_port, egress_port, test_timeout):
40
Rich Lane9a003812012-10-04 17:17:59 -070041 logging.info("Sending packet to dp port " + str(ingress_port) +
Ken Chiangaa5bc062012-03-31 14:03:28 -070042 ", expecting output on " + str(egress_port))
43 obj.dataplane.send(ingress_port, str(pkt))
44
45 exp_pkt_arg = None
46 exp_port = None
Rich Lane477f4812012-10-04 22:49:00 -070047 if config["relax"]:
Ken Chiangaa5bc062012-03-31 14:03:28 -070048 exp_pkt_arg = pkt
49 exp_port = egress_port
50
Rich Lanec8aaa3e2012-07-26 19:28:02 -070051 (rcv_port, rcv_pkt, pkt_time) = obj.dataplane.poll(port_number=exp_port,
Ken Chiangaa5bc062012-03-31 14:03:28 -070052 exp_pkt=exp_pkt_arg)
53 obj.assertTrue(rcv_pkt is not None,
54 "Packet not received on port " + str(egress_port))
Rich Lane9a003812012-10-04 17:17:59 -070055 logging.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
Ken Chiangaa5bc062012-03-31 14:03:28 -070056 str(rcv_port))
57 obj.assertEqual(rcv_port, egress_port,
58 "Packet received on port " + str(rcv_port) +
59 ", expected port " + str(egress_port))
60 obj.assertEqual(str(pkt), str(rcv_pkt),
61 'Response packet does not match send packet')
62
Rich Laneb90a1c42012-10-05 09:16:05 -070063class SingleFlowStats(base_tests.SimpleDataPlane):
Dan Talayco89d57342010-06-07 16:24:59 -070064 """
65 Verify flow stats are properly retrieved.
66
67 Generate a packet
Ken Chiang620bdcc2012-03-23 12:52:07 -070068 Generate and install a matching flow
69 Send the packet
70 Send a flow stats request to match the flow and retrieve stats
71 Verify that the packet counter has incremented
Dan Talayco89d57342010-06-07 16:24:59 -070072 """
Ken Chiang620bdcc2012-03-23 12:52:07 -070073
Rich Lanec42dbad2012-10-11 10:57:44 -070074 def verifyStats(self, flow_mod_msg, match, out_port, test_timeout, packet_count):
Rich Lane28fa9272013-03-08 16:00:25 -080075 stat_req = ofp.message.flow_stats_request()
Ken Chiang620bdcc2012-03-23 12:52:07 -070076 stat_req.match = match
77 stat_req.table_id = 0xff
78 stat_req.out_port = out_port
79
80 all_packets_received = 0
81 for i in range(0,test_timeout):
Rich Lane9a003812012-10-04 17:17:59 -070082 logging.info("Sending stats request")
Ken Chiangfb593e72012-03-28 17:19:13 -070083 response, pkt = self.controller.transact(stat_req,
84 timeout=test_timeout)
85 self.assertTrue(response is not None,
86 "No response to stats request")
87 self.assertTrue(len(response.stats) == 1,
88 "Did not receive flow stats reply")
Ken Chiang620bdcc2012-03-23 12:52:07 -070089 for obj in response.stats:
90 # TODO: pad1 and pad2 fields may be nonzero, is this a bug?
91 # for now, just clear them so the assert is simpler
Rich Lanec42dbad2012-10-11 10:57:44 -070092 obj.match.pad1 = 0
93 obj.match.pad2 = [0, 0]
94 self.assertEqual(flow_mod_msg.match, obj.match,
95 "Matches do not match")
96 self.assertEqual(obj.cookie, flow_mod_msg.cookie)
97 self.assertEqual(obj.priority, flow_mod_msg.priority)
98 self.assertEqual(obj.idle_timeout, flow_mod_msg.idle_timeout)
99 self.assertEqual(obj.hard_timeout, flow_mod_msg.hard_timeout)
100 self.assertEqual(obj.actions, flow_mod_msg.actions)
Rich Lane9a003812012-10-04 17:17:59 -0700101 logging.info("Received " + str(obj.packet_count) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700102 if obj.packet_count == packet_count:
103 all_packets_received = 1
104
105 if all_packets_received:
106 break
107 sleep(1)
108
109 self.assertTrue(all_packets_received,
110 "Packet count does not match number sent")
111
Dan Talayco89d57342010-06-07 16:24:59 -0700112 def runTest(self):
Ken Chiang620bdcc2012-03-23 12:52:07 -0700113 # TODO: set from command-line parameter
114 test_timeout = 60
115
Rich Lane477f4812012-10-04 22:49:00 -0700116 of_ports = config["port_map"].keys()
Dan Talayco89d57342010-06-07 16:24:59 -0700117 of_ports.sort()
118 self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
119
Rich Lane32bf9482013-01-03 17:26:30 -0800120 delete_all_flows(self.controller)
Dan Talayco89d57342010-06-07 16:24:59 -0700121
Ken Chiang620bdcc2012-03-23 12:52:07 -0700122 # build packet
Dan Talayco89d57342010-06-07 16:24:59 -0700123 pkt = simple_tcp_packet()
Ed Swierk99a74de2012-08-22 06:40:54 -0700124 match = packet_to_flow_match(self, pkt)
Dan Talayco89d57342010-06-07 16:24:59 -0700125 match.wildcards &= ~ofp.OFPFW_IN_PORT
126 self.assertTrue(match is not None,
127 "Could not generate flow match from pkt")
Rich Lane9d3cc6b2013-03-08 16:33:08 -0800128 act = ofp.action.output()
Dan Talayco89d57342010-06-07 16:24:59 -0700129
Ken Chiang620bdcc2012-03-23 12:52:07 -0700130 # build flow
Dan Talayco39bf6912010-07-08 14:17:52 -0700131 ingress_port = of_ports[0];
132 egress_port = of_ports[1];
Rich Lane9a003812012-10-04 17:17:59 -0700133 logging.info("Ingress " + str(ingress_port) +
Dan Talayco89d57342010-06-07 16:24:59 -0700134 " to egress " + str(egress_port))
Ken Chiang620bdcc2012-03-23 12:52:07 -0700135 match.in_port = ingress_port
Rich Lane28fa9272013-03-08 16:00:25 -0800136 flow_mod_msg = ofp.message.flow_mod()
Rich Lanec42dbad2012-10-11 10:57:44 -0700137 flow_mod_msg.match = copy.deepcopy(match)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700138 flow_mod_msg.cookie = random.randint(0,9007199254740992)
139 flow_mod_msg.buffer_id = 0xffffffff
Rich Lanec42dbad2012-10-11 10:57:44 -0700140 flow_mod_msg.idle_timeout = 60000
141 flow_mod_msg.hard_timeout = 65000
142 flow_mod_msg.priority = 100
Ken Chiang620bdcc2012-03-23 12:52:07 -0700143 act.port = egress_port
Rich Lanec495d9e2013-03-08 17:43:36 -0800144 flow_mod_msg.actions.append(act)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700145
146 # send flow
Rich Lane9a003812012-10-04 17:17:59 -0700147 logging.info("Inserting flow")
Rich Lane5c3151c2013-01-03 17:15:41 -0800148 self.controller.message_send(flow_mod_msg)
Rich Lane3a261d52013-01-03 17:45:08 -0800149 do_barrier(self.controller)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700150
151 # no packets sent, so zero packet count
Rich Lanec42dbad2012-10-11 10:57:44 -0700152 self.verifyStats(flow_mod_msg, match, ofp.OFPP_NONE, test_timeout, 0)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700153
154 # send packet N times
155 num_sends = random.randint(10,20)
Rich Lane9a003812012-10-04 17:17:59 -0700156 logging.info("Sending " + str(num_sends) + " test packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700157 for i in range(0,num_sends):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700158 sendPacket(self, pkt, ingress_port, egress_port,
159 test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700160
Rich Lanec42dbad2012-10-11 10:57:44 -0700161 self.verifyStats(flow_mod_msg, match, ofp.OFPP_NONE, test_timeout, num_sends)
162 self.verifyStats(flow_mod_msg, match, egress_port, test_timeout, num_sends)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700163 for wc in WILDCARD_VALUES:
Ed Swierk99a74de2012-08-22 06:40:54 -0700164 match.wildcards = required_wildcards(self) | wc
Rich Lanec42dbad2012-10-11 10:57:44 -0700165 self.verifyStats(flow_mod_msg, match, egress_port, test_timeout, num_sends)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700166
167
Rich Laneb90a1c42012-10-05 09:16:05 -0700168class TwoFlowStats(base_tests.SimpleDataPlane):
Ken Chiang620bdcc2012-03-23 12:52:07 -0700169 """
170 Verify flow stats are properly retrieved.
171
172 Generate two packets and install two matching flows
173 Send some number of packets
174 Send a flow stats request to match the flows and retrieve stats
175 Verify that the packet counter has incremented
176
177 TODO: add a third flow, and then configure the match to exclude
178 that flow?
179 """
180
181 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
Ed Swierk99a74de2012-08-22 06:40:54 -0700182 match = packet_to_flow_match(self, pkt)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700183 match.wildcards &= ~ofp.OFPFW_IN_PORT
184 self.assertTrue(match is not None,
185 "Could not generate flow match from pkt")
Dan Talayco89d57342010-06-07 16:24:59 -0700186 match.in_port = ingress_port
187
Rich Lane28fa9272013-03-08 16:00:25 -0800188 flow_mod_msg = ofp.message.flow_mod()
Dan Talayco89d57342010-06-07 16:24:59 -0700189 flow_mod_msg.match = match
190 flow_mod_msg.cookie = random.randint(0,9007199254740992)
191 flow_mod_msg.buffer_id = 0xffffffff
Ken Chiang620bdcc2012-03-23 12:52:07 -0700192 flow_mod_msg.idle_timeout = 0
193 flow_mod_msg.hard_timeout = 0
Rich Lane9d3cc6b2013-03-08 16:33:08 -0800194 act = ofp.action.output()
Dan Talayco89d57342010-06-07 16:24:59 -0700195 act.port = egress_port
Rich Lanec495d9e2013-03-08 17:43:36 -0800196 flow_mod_msg.actions.append(act)
Dan Talayco89d57342010-06-07 16:24:59 -0700197
Rich Lane9a003812012-10-04 17:17:59 -0700198 logging.info("Ingress " + str(ingress_port) +
Ken Chiang620bdcc2012-03-23 12:52:07 -0700199 " to egress " + str(egress_port))
Dan Talayco89d57342010-06-07 16:24:59 -0700200
Ken Chiang620bdcc2012-03-23 12:52:07 -0700201 return flow_mod_msg
Dan Talayco89d57342010-06-07 16:24:59 -0700202
Ken Chiangaa5bc062012-03-31 14:03:28 -0700203 def sumStatsReplyCounts(self, response):
204 total_packets = 0
205 for obj in response.stats:
206 # TODO: pad1 and pad2 fields may be nonzero, is this a bug?
207 # for now, just clear them so the assert is simpler
208 #obj.match.pad1 = 0
209 #obj.match.pad2 = [0, 0]
210 #self.assertEqual(match, obj.match,
211 # "Matches do not match")
Rich Lane9a003812012-10-04 17:17:59 -0700212 logging.info("Received " + str(obj.packet_count)
Ken Chiangaa5bc062012-03-31 14:03:28 -0700213 + " packets")
214 total_packets += obj.packet_count
215 return total_packets
Ken Chiang620bdcc2012-03-23 12:52:07 -0700216
217 def verifyStats(self, match, out_port, test_timeout, packet_count):
Rich Lane28fa9272013-03-08 16:00:25 -0800218 stat_req = ofp.message.flow_stats_request()
Ken Chiang620bdcc2012-03-23 12:52:07 -0700219 stat_req.match = match
220 stat_req.table_id = 0xff
221 stat_req.out_port = out_port
222
223 all_packets_received = 0
224 for i in range(0,test_timeout):
Rich Lane9a003812012-10-04 17:17:59 -0700225 logging.info("Sending stats request")
Ken Chiangaa5bc062012-03-31 14:03:28 -0700226 # TODO: move REPLY_MORE handling to controller.transact?
Ken Chiangfb593e72012-03-28 17:19:13 -0700227 response, pkt = self.controller.transact(stat_req,
228 timeout=test_timeout)
229 self.assertTrue(response is not None,
230 "No response to stats request")
Ken Chiangaa5bc062012-03-31 14:03:28 -0700231 total_packets = self.sumStatsReplyCounts(response)
232
233 while response.flags == ofp.OFPSF_REPLY_MORE:
234 response, pkt = self.controller.poll(exp_msg=
235 ofp.OFPT_STATS_REPLY,
236 timeout=test_timeout)
237 total_packets += self.sumStatsReplyCounts(response)
238
Ken Chiang620bdcc2012-03-23 12:52:07 -0700239 if total_packets == packet_count:
240 all_packets_received = 1
241 break
242 sleep(1)
243
244 self.assertTrue(all_packets_received,
Ken Chiangaa5bc062012-03-31 14:03:28 -0700245 "Total stats packet count " + str(total_packets) +
246 " does not match number sent " + str(packet_count))
Ken Chiang620bdcc2012-03-23 12:52:07 -0700247
248 def runTest(self):
Ken Chiang620bdcc2012-03-23 12:52:07 -0700249 # TODO: set from command-line parameter
250 test_timeout = 60
251
Rich Lane477f4812012-10-04 22:49:00 -0700252 of_ports = config["port_map"].keys()
Ken Chiang620bdcc2012-03-23 12:52:07 -0700253 of_ports.sort()
254 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
255 ingress_port = of_ports[0];
256 egress_port1 = of_ports[1];
257 egress_port2 = of_ports[2];
258
Rich Lane32bf9482013-01-03 17:26:30 -0800259 delete_all_flows(self.controller)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700260
261 pkt1 = simple_tcp_packet()
262 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
263
264 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
265 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
266
Rich Lane9a003812012-10-04 17:17:59 -0700267 logging.info("Inserting flow1")
Rich Lane5c3151c2013-01-03 17:15:41 -0800268 self.controller.message_send(flow_mod_msg1)
Rich Lane9a003812012-10-04 17:17:59 -0700269 logging.info("Inserting flow2")
Rich Lane5c3151c2013-01-03 17:15:41 -0800270 self.controller.message_send(flow_mod_msg2)
Rich Lane3a261d52013-01-03 17:45:08 -0800271 do_barrier(self.controller)
Dan Talayco89d57342010-06-07 16:24:59 -0700272
Ken Chiang620bdcc2012-03-23 12:52:07 -0700273 num_pkt1s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700274 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700275 num_pkt2s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700276 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700277 for i in range(0,num_pkt1s):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700278 sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700279 for i in range(0,num_pkt2s):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700280 sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700281
Ed Swierk99a74de2012-08-22 06:40:54 -0700282 match1 = packet_to_flow_match(self, pkt1)
Rich Lane9a003812012-10-04 17:17:59 -0700283 logging.info("Verifying flow1's " + str(num_pkt1s) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700284 self.verifyStats(match1, ofp.OFPP_NONE, test_timeout, num_pkt1s)
Ed Swierk99a74de2012-08-22 06:40:54 -0700285 match2 = packet_to_flow_match(self, pkt2)
Rich Lane9a003812012-10-04 17:17:59 -0700286 logging.info("Verifying flow2's " + str(num_pkt2s) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700287 self.verifyStats(match2, ofp.OFPP_NONE, test_timeout, num_pkt2s)
288 match1.wildcards |= ofp.OFPFW_DL_SRC
Rich Lane9a003812012-10-04 17:17:59 -0700289 logging.info("Verifying combined " + str(num_pkt1s+num_pkt2s) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700290 self.verifyStats(match1, ofp.OFPP_NONE, test_timeout,
291 num_pkt1s+num_pkt2s)
292 # TODO: sweep through the wildcards to verify matching?
293
294
Rich Laneb90a1c42012-10-05 09:16:05 -0700295class AggregateStats(base_tests.SimpleDataPlane):
Ken Chiang620bdcc2012-03-23 12:52:07 -0700296 """
297 Verify aggregate flow stats are properly retrieved.
298
299 Generate two packets
300 Generate and install two matching flows
301 Send an aggregate stats request
302 Verify that aggregate stats are correct
303 Also verify out_port filtering
304 """
305
306 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
Ed Swierk99a74de2012-08-22 06:40:54 -0700307 match = packet_to_flow_match(self, pkt)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700308 match.wildcards &= ~ofp.OFPFW_IN_PORT
309 self.assertTrue(match is not None,
310 "Could not generate flow match from pkt")
311 match.in_port = ingress_port
312
Rich Lane28fa9272013-03-08 16:00:25 -0800313 flow_mod_msg = ofp.message.flow_mod()
Ken Chiang620bdcc2012-03-23 12:52:07 -0700314 flow_mod_msg.match = match
315 flow_mod_msg.cookie = random.randint(0,9007199254740992)
316 flow_mod_msg.buffer_id = 0xffffffff
317 flow_mod_msg.idle_timeout = 0
318 flow_mod_msg.hard_timeout = 0
Rich Lane9d3cc6b2013-03-08 16:33:08 -0800319 act = ofp.action.output()
Ken Chiang620bdcc2012-03-23 12:52:07 -0700320 act.port = egress_port
Rich Lanec495d9e2013-03-08 17:43:36 -0800321 flow_mod_msg.actions.append(act)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700322
Rich Lane9a003812012-10-04 17:17:59 -0700323 logging.info("Ingress " + str(ingress_port) +
Ken Chiang620bdcc2012-03-23 12:52:07 -0700324 " to egress " + str(egress_port))
325
326 return flow_mod_msg
327
Ken Chiang620bdcc2012-03-23 12:52:07 -0700328 def verifyAggFlowStats(self, match, out_port, test_timeout,
329 flow_count, packet_count):
Rich Lane28fa9272013-03-08 16:00:25 -0800330 stat_req = ofp.message.aggregate_stats_request()
Ken Chiang620bdcc2012-03-23 12:52:07 -0700331 stat_req.match = match
332 stat_req.table_id = 0xff
333 stat_req.out_port = out_port
334
335 all_packets_received = 0
336 for i in range(0,test_timeout):
Rich Lane9a003812012-10-04 17:17:59 -0700337 logging.info("Sending stats request")
Ken Chiangfb593e72012-03-28 17:19:13 -0700338 response, pkt = self.controller.transact(stat_req,
339 timeout=test_timeout)
340 self.assertTrue(response is not None,
341 "No response to stats request")
Dan Talaycoaff26c82012-03-25 15:06:26 -0700342 self.assertTrue(len(response.stats) == 1,
343 "Did not receive flow stats reply")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700344 for obj in response.stats:
345 self.assertTrue(obj.flow_count == flow_count,
346 "Flow count " + str(obj.flow_count) +
347 " does not match expected " + str(flow_count))
Rich Lane9a003812012-10-04 17:17:59 -0700348 logging.info("Received " + str(obj.packet_count) + " packets")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700349 if obj.packet_count == packet_count:
350 all_packets_received = 1
351
352 if all_packets_received:
353 break
354 sleep(1)
355
356 self.assertTrue(all_packets_received,
357 "Packet count does not match number sent")
358
359 def runTest(self):
Ken Chiang620bdcc2012-03-23 12:52:07 -0700360 # TODO: set from command-line parameter
361 test_timeout = 60
362
Rich Lane477f4812012-10-04 22:49:00 -0700363 of_ports = config["port_map"].keys()
Ken Chiang620bdcc2012-03-23 12:52:07 -0700364 of_ports.sort()
365 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
366 ingress_port = of_ports[0];
367 egress_port1 = of_ports[1];
368 egress_port2 = of_ports[2];
369
Rich Lane32bf9482013-01-03 17:26:30 -0800370 delete_all_flows(self.controller)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700371
372 pkt1 = simple_tcp_packet()
373 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
374
375 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
376 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
377
Rich Lane9a003812012-10-04 17:17:59 -0700378 logging.info("Inserting flow1")
Rich Lane5c3151c2013-01-03 17:15:41 -0800379 self.controller.message_send(flow_mod_msg1)
Rich Lane9a003812012-10-04 17:17:59 -0700380 logging.info("Inserting flow2")
Rich Lane5c3151c2013-01-03 17:15:41 -0800381 self.controller.message_send(flow_mod_msg2)
Rich Lane3a261d52013-01-03 17:45:08 -0800382 do_barrier(self.controller)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700383
384 num_pkt1s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700385 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700386 num_pkt2s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700387 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
Ken Chiang620bdcc2012-03-23 12:52:07 -0700388 for i in range(0,num_pkt1s):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700389 sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700390 for i in range(0,num_pkt2s):
Ken Chiangaa5bc062012-03-31 14:03:28 -0700391 sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700392
393 # loop on flow stats request until timeout
Ed Swierk99a74de2012-08-22 06:40:54 -0700394 match = packet_to_flow_match(self, pkt1)
Ken Chiang620bdcc2012-03-23 12:52:07 -0700395 match.wildcards |= ofp.OFPFW_DL_SRC
396 self.verifyAggFlowStats(match, ofp.OFPP_NONE, test_timeout,
397 2, num_pkt1s+num_pkt2s)
398
399 # out_port filter for egress_port1
400 self.verifyAggFlowStats(match, egress_port1, test_timeout,
401 1, num_pkt1s)
402
403 # out_port filter for egress_port1
404 self.verifyAggFlowStats(match, egress_port2, test_timeout,
405 1, num_pkt2s)
Rich Lane97a5f502012-10-11 09:28:11 -0700406
Rich Lanef062b3d2012-10-11 09:53:19 -0700407class EmptyFlowStats(base_tests.SimpleDataPlane):
408 """
409 Verify the switch replies to a flow stats request when
410 the query doesn't match any flows.
411 """
412 def runTest(self):
Rich Lane32bf9482013-01-03 17:26:30 -0800413 delete_all_flows(self.controller)
Rich Lanef062b3d2012-10-11 09:53:19 -0700414 match = ofp.ofp_match()
415 match.wildcards = 0
Rich Lane28fa9272013-03-08 16:00:25 -0800416 stat_req = ofp.message.flow_stats_request()
Rich Lanef062b3d2012-10-11 09:53:19 -0700417 stat_req.match = match
418 stat_req.table_id = 0xff
419 stat_req.out_port = ofp.OFPP_NONE
420
421 response, pkt = self.controller.transact(stat_req)
422 self.assertTrue(response is not None,
423 "No response to stats request")
424 self.assertEquals(len(response.stats), 0)
425 self.assertEquals(response.flags, 0)
426
Rich Lane97a5f502012-10-11 09:28:11 -0700427class EmptyAggregateStats(base_tests.SimpleDataPlane):
428 """
429 Verify aggregate flow stats are properly retrieved when
430 the query doesn't match any flows.
431 """
432 def runTest(self):
Rich Lane32bf9482013-01-03 17:26:30 -0800433 delete_all_flows(self.controller)
Rich Lane97a5f502012-10-11 09:28:11 -0700434 match = ofp.ofp_match()
435 match.wildcards = 0
Rich Lane28fa9272013-03-08 16:00:25 -0800436 stat_req = ofp.message.aggregate_stats_request()
Rich Lane97a5f502012-10-11 09:28:11 -0700437 stat_req.match = match
438 stat_req.table_id = 0xff
439 stat_req.out_port = ofp.OFPP_NONE
440
441 response, pkt = self.controller.transact(stat_req)
442 self.assertTrue(response is not None,
443 "No response to stats request")
444 self.assertTrue(len(response.stats) == 1,
445 "Did not receive flow stats reply")
446 self.assertEquals(response.stats[0].flow_count, 0)
447 self.assertEquals(response.stats[0].packet_count, 0)
448 self.assertEquals(response.stats[0].byte_count, 0)
Rich Laned10e8e02012-11-21 15:48:36 -0800449
450class DeletedFlowStats(base_tests.SimpleDataPlane):
451 """
452 Verify flow stats are properly returned when a flow is deleted.
453
454 Generate a packet
455 Generate and install a matching flow
456 Send the packet
457 Delete the flow
458 Verify that the flow_removed message has the correct stats
459 """
460
461 def runTest(self):
462 # TODO: set from command-line parameter
463 test_timeout = 60
464
465 of_ports = config["port_map"].keys()
466 of_ports.sort()
467 self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
468
Rich Lane32bf9482013-01-03 17:26:30 -0800469 delete_all_flows(self.controller)
Rich Laned10e8e02012-11-21 15:48:36 -0800470
471 # build packet
472 pkt = simple_tcp_packet()
473 match = packet_to_flow_match(self, pkt)
474 match.wildcards &= ~ofp.OFPFW_IN_PORT
475 self.assertTrue(match is not None,
476 "Could not generate flow match from pkt")
Rich Lane9d3cc6b2013-03-08 16:33:08 -0800477 act = ofp.action.output()
Rich Laned10e8e02012-11-21 15:48:36 -0800478
479 # build flow
480 ingress_port = of_ports[0];
481 egress_port = of_ports[1];
482 logging.info("Ingress " + str(ingress_port) +
483 " to egress " + str(egress_port))
484 match.in_port = ingress_port
Rich Lane28fa9272013-03-08 16:00:25 -0800485 flow_mod_msg = ofp.message.flow_mod()
Rich Laned10e8e02012-11-21 15:48:36 -0800486 flow_mod_msg.match = copy.deepcopy(match)
487 flow_mod_msg.cookie = random.randint(0,9007199254740992)
488 flow_mod_msg.buffer_id = 0xffffffff
489 flow_mod_msg.idle_timeout = 0
490 flow_mod_msg.hard_timeout = 0
491 flow_mod_msg.priority = 100
492 flow_mod_msg.flags = ofp.OFPFF_SEND_FLOW_REM
493 act.port = egress_port
Rich Lanec495d9e2013-03-08 17:43:36 -0800494 flow_mod_msg.actions.append(act)
Rich Laned10e8e02012-11-21 15:48:36 -0800495
496 # send flow
497 logging.info("Inserting flow")
Rich Lane5c3151c2013-01-03 17:15:41 -0800498 self.controller.message_send(flow_mod_msg)
Rich Lane3a261d52013-01-03 17:45:08 -0800499 do_barrier(self.controller)
Rich Laned10e8e02012-11-21 15:48:36 -0800500
501 # send packet N times
502 num_sends = random.randint(10,20)
503 logging.info("Sending " + str(num_sends) + " test packets")
504 for i in range(0,num_sends):
505 sendPacket(self, pkt, ingress_port, egress_port,
506 test_timeout)
507
Ken Chiang626ce1d2013-01-09 11:58:50 -0800508 # wait some time for flow stats to be propagated
509 # FIXME timeout handling should be unified
510 sleep(test_param_get('default_timeout', default=2))
511
Rich Laned10e8e02012-11-21 15:48:36 -0800512 # delete flow
513 logging.info("Deleting flow")
Rich Lane32bf9482013-01-03 17:26:30 -0800514 delete_all_flows(self.controller)
Rich Laned10e8e02012-11-21 15:48:36 -0800515
516 # wait for flow_removed message
517 flow_removed, _ = self.controller.poll(
518 exp_msg=ofp.OFPT_FLOW_REMOVED, timeout=test_timeout)
519
520 self.assertTrue(flow_removed != None, "Did not receive flow_removed message")
Ken Chiang626ce1d2013-01-09 11:58:50 -0800521 self.assertEqual(flow_removed.cookie, flow_mod_msg.cookie,
522 "Received cookie " + str(flow_removed.cookie) +
523 " does not match expected " + str(flow_mod_msg.cookie))
524 self.assertEqual(flow_removed.reason, ofp.OFPRR_DELETE,
525 "Received reason " + str(flow_removed.reason) +
526 " does not match expected " + str(ofp.OFPRR_DELETE))
527 self.assertEqual(flow_removed.packet_count, num_sends,
528 "Received packet count " + str(flow_removed.packet_count) +
529 " does not match expected " + str(num_sends))
530 tolerance = 5 # percent
531 self.assertTrue(flow_removed.byte_count >=
532 (1-tolerance/100.0) * num_sends * len(str(pkt)) and
533 flow_removed.byte_count <=
534 (1+tolerance/100.0) * num_sends * len(str(pkt)),
535 "Received byte count " + str(flow_removed.byte_count) +
536 " is not within " + str(tolerance) + "% of expected " +
537 str(num_sends*len(str(pkt))))