blob: 8aeefa41014a4b12250bc53cc61f3dc28ed28092 [file] [log] [blame]
Shudong Zhoudf510a82012-08-03 18:08:40 -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
Rich Lane477f4812012-10-04 22:49:00 -070012from oftest import config
Shudong Zhoudf510a82012-08-03 18:08:40 -070013import oftest.controller as controller
Rich Laned7b0ffa2013-03-08 15:53:42 -080014import ofp
Shudong Zhoudf510a82012-08-03 18:08:40 -070015import oftest.dataplane as dataplane
Shudong Zhoudf510a82012-08-03 18:08:40 -070016import oftest.parse as parse
Rich Laneb90a1c42012-10-05 09:16:05 -070017import oftest.base_tests as base_tests
Shudong Zhoudf510a82012-08-03 18:08:40 -070018
Rich Laneda3b5ad2012-10-03 09:05:32 -070019from oftest.testutils import *
Shudong Zhoudf510a82012-08-03 18:08:40 -070020from time import sleep
21
Shudong Zhoudf510a82012-08-03 18:08:40 -070022# TODO: ovs has problems with VLAN id?
23WILDCARD_VALUES = [ofp.OFPFW_IN_PORT,
24 # (ofp.OFPFW_DL_VLAN | ofp.OFPFW_DL_VLAN_PCP),
25 ofp.OFPFW_DL_SRC,
26 ofp.OFPFW_DL_DST,
27 (ofp.OFPFW_DL_TYPE | ofp.OFPFW_NW_SRC_ALL |
28 ofp.OFPFW_NW_DST_ALL | ofp.OFPFW_NW_TOS | ofp.OFPFW_NW_PROTO |
29 ofp.OFPFW_TP_SRC | ofp.OFPFW_TP_DST),
30 (ofp.OFPFW_NW_PROTO | ofp.OFPFW_TP_SRC | ofp.OFPFW_TP_DST),
31 ofp.OFPFW_TP_SRC,
32 ofp.OFPFW_TP_DST,
33 ofp.OFPFW_NW_SRC_MASK,
34 ofp.OFPFW_NW_DST_MASK,
35 ofp.OFPFW_DL_VLAN_PCP,
36 ofp.OFPFW_NW_TOS]
37
Shudong Zhoudf510a82012-08-03 18:08:40 -070038def sendPacket(obj, pkt, ingress_port, egress_port, test_timeout):
39
Rich Lane9a003812012-10-04 17:17:59 -070040 logging.info("Sending packet to dp port " + str(ingress_port) +
Shudong Zhoudf510a82012-08-03 18:08:40 -070041 ", expecting output on " + str(egress_port))
42 obj.dataplane.send(ingress_port, str(pkt))
43
44 exp_pkt_arg = None
45 exp_port = None
Rich Lane477f4812012-10-04 22:49:00 -070046 if config["relax"]:
Shudong Zhoudf510a82012-08-03 18:08:40 -070047 exp_pkt_arg = pkt
48 exp_port = egress_port
49
50 (rcv_port, rcv_pkt, pkt_time) = obj.dataplane.poll(port_number=exp_port,
51 exp_pkt=exp_pkt_arg)
52 obj.assertTrue(rcv_pkt is not None,
53 "Packet not received on port " + str(egress_port))
Rich Lane9a003812012-10-04 17:17:59 -070054 logging.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
Shudong Zhoudf510a82012-08-03 18:08:40 -070055 str(rcv_port))
56 obj.assertEqual(rcv_port, egress_port,
57 "Packet received on port " + str(rcv_port) +
58 ", expected port " + str(egress_port))
59 obj.assertEqual(str(pkt), str(rcv_pkt),
60 'Response packet does not match send packet')
61
Shudong Zhou50051c72012-08-06 16:53:46 -070062def getStats(obj, port):
Rich Lane28fa9272013-03-08 16:00:25 -080063 stat_req = ofp.message.port_stats_request()
Shudong Zhou50051c72012-08-06 16:53:46 -070064 stat_req.port_no = port
65
Rich Lane9a003812012-10-04 17:17:59 -070066 logging.info("Sending stats request")
Shudong Zhou50051c72012-08-06 16:53:46 -070067 response, pkt = obj.controller.transact(stat_req, timeout=2)
68 obj.assertTrue(response is not None,
69 "No response to stats request")
70 obj.assertTrue(len(response.stats) == 1,
71 "Did not receive port stats reply")
72 for item in response.stats:
Rich Lane9a003812012-10-04 17:17:59 -070073 logging.info("Sent " + str(item.tx_packets) + " packets")
Shudong Zhou50051c72012-08-06 16:53:46 -070074 packet_sent = item.tx_packets
75 packet_recv = item.rx_packets
Rich Lane9a003812012-10-04 17:17:59 -070076 logging.info("Port %d stats count: tx %d rx %d" % (port, packet_sent, packet_recv))
Shudong Zhou50051c72012-08-06 16:53:46 -070077 return packet_sent, packet_recv
78
Shudong Zhou1a5b0822012-10-29 22:03:34 -070079def getAllStats(obj):
Rich Lane28fa9272013-03-08 16:00:25 -080080 stat_req = ofp.message.port_stats_request()
Shudong Zhou1a5b0822012-10-29 22:03:34 -070081 stat_req.port_no = ofp.OFPP_NONE
82
83 logging.info("Sending all port stats request")
84 response, pkt = obj.controller.transact(stat_req, timeout=2)
85 obj.assertTrue(response is not None,
86 "No response to stats request")
87 obj.assertTrue(len(response.stats) >= 3,
88 "Did not receive all port stats reply")
89 stats = {}
90 for item in response.stats:
91 stats[ item.port_no ] = ( item.tx_packets, item.rx_packets )
92 return stats
93
Shudong Zhou50051c72012-08-06 16:53:46 -070094def verifyStats(obj, port, test_timeout, packet_sent, packet_recv):
Rich Lane28fa9272013-03-08 16:00:25 -080095 stat_req = ofp.message.port_stats_request()
Shudong Zhou50051c72012-08-06 16:53:46 -070096 stat_req.port_no = port
97
98 all_packets_received = 0
99 all_packets_sent = 0
100 sent = recv = 0
101 for i in range(0,test_timeout):
Rich Lane9a003812012-10-04 17:17:59 -0700102 logging.info("Sending stats request")
Shudong Zhou50051c72012-08-06 16:53:46 -0700103 response, pkt = obj.controller.transact(stat_req,
104 timeout=test_timeout)
105 obj.assertTrue(response is not None,
106 "No response to stats request")
107 obj.assertTrue(len(response.stats) == 1,
108 "Did not receive port stats reply")
109 for item in response.stats:
110 sent = item.tx_packets
111 recv = item.rx_packets
Rich Lane9a003812012-10-04 17:17:59 -0700112 logging.info("Sent " + str(item.tx_packets) + " packets")
Shudong Zhou50051c72012-08-06 16:53:46 -0700113 if item.tx_packets == packet_sent:
114 all_packets_sent = 1
Rich Lane9a003812012-10-04 17:17:59 -0700115 logging.info("Received " + str(item.rx_packets) + " packets")
Shudong Zhou50051c72012-08-06 16:53:46 -0700116 if item.rx_packets == packet_recv:
117 all_packets_received = 1
118
119 if all_packets_received and all_packets_sent:
120 break
121 sleep(1)
122
Rich Lane9a003812012-10-04 17:17:59 -0700123 logging.info("Expected port %d stats count: tx %d rx %d" % (port, packet_sent, packet_recv))
124 logging.info("Actual port %d stats count: tx %d rx %d" % (port, sent, recv))
Shudong Zhou50051c72012-08-06 16:53:46 -0700125 obj.assertTrue(all_packets_sent,
126 "Packet sent does not match number sent")
127 obj.assertTrue(all_packets_received,
128 "Packet received does not match number sent")
129
Rich Lane97e99652013-01-02 17:23:20 -0800130@group('smoke')
Rich Laneb90a1c42012-10-05 09:16:05 -0700131class SingleFlowStats(base_tests.SimpleDataPlane):
Shudong Zhoudf510a82012-08-03 18:08:40 -0700132 """
133 Verify flow stats are properly retrieved.
134
135 Generate a packet
Shudong Zhou1d423922012-08-04 16:45:02 -0700136 Generate and install a flow from port 1 to 2
Shudong Zhoudf510a82012-08-03 18:08:40 -0700137 Send the packet
Shudong Zhou1d423922012-08-04 16:45:02 -0700138 Send port stats request to port 1 & 2
Shudong Zhoudf510a82012-08-03 18:08:40 -0700139 Verify that the packet counter has incremented
140 """
141
Shudong Zhoudf510a82012-08-03 18:08:40 -0700142 def runTest(self):
Shudong Zhoudf510a82012-08-03 18:08:40 -0700143 # TODO: set from command-line parameter
144 test_timeout = 60
145
Rich Lane477f4812012-10-04 22:49:00 -0700146 of_ports = config["port_map"].keys()
Shudong Zhoudf510a82012-08-03 18:08:40 -0700147 of_ports.sort()
148 self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
149
Rich Lane32bf9482013-01-03 17:26:30 -0800150 delete_all_flows(self.controller)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700151
152 # build packet
153 pkt = simple_tcp_packet()
Ed Swierk99a74de2012-08-22 06:40:54 -0700154 match = packet_to_flow_match(self, pkt)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700155 match.wildcards &= ~ofp.OFPFW_IN_PORT
156 self.assertTrue(match is not None,
157 "Could not generate flow match from pkt")
Rich Lane9d3cc6b2013-03-08 16:33:08 -0800158 act = ofp.action.output()
Shudong Zhoudf510a82012-08-03 18:08:40 -0700159
160 # build flow
161 ingress_port = of_ports[0];
162 egress_port = of_ports[1];
Rich Lane9a003812012-10-04 17:17:59 -0700163 logging.info("Ingress " + str(ingress_port) +
Shudong Zhoudf510a82012-08-03 18:08:40 -0700164 " to egress " + str(egress_port))
165 match.in_port = ingress_port
Rich Lane28fa9272013-03-08 16:00:25 -0800166 flow_mod_msg = ofp.message.flow_mod()
Shudong Zhoudf510a82012-08-03 18:08:40 -0700167 flow_mod_msg.match = match
168 flow_mod_msg.cookie = random.randint(0,9007199254740992)
169 flow_mod_msg.buffer_id = 0xffffffff
170 flow_mod_msg.idle_timeout = 0
171 flow_mod_msg.hard_timeout = 0
172 act.port = egress_port
Rich Lanee30455b2013-01-03 16:24:44 -0800173 flow_mod_msg.actions.add(act)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700174
175 # send flow
Rich Lane9a003812012-10-04 17:17:59 -0700176 logging.info("Inserting flow")
Rich Lane5c3151c2013-01-03 17:15:41 -0800177 self.controller.message_send(flow_mod_msg)
Rich Lane3a261d52013-01-03 17:45:08 -0800178 do_barrier(self.controller)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700179
Shudong Zhou50051c72012-08-06 16:53:46 -0700180 # get initial port stats count
181 initTxInPort, initRxInPort = getStats(self, ingress_port)
182 initTxOutPort, initRxOutPort = getStats(self, egress_port)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700183
184 # send packet N times
185 num_sends = random.randint(10,20)
Rich Lane9a003812012-10-04 17:17:59 -0700186 logging.info("Sending " + str(num_sends) + " test packets")
Shudong Zhoudf510a82012-08-03 18:08:40 -0700187 for i in range(0,num_sends):
188 sendPacket(self, pkt, ingress_port, egress_port,
189 test_timeout)
190
Shudong Zhou50051c72012-08-06 16:53:46 -0700191 verifyStats(self, ingress_port, test_timeout, initTxInPort, initRxInPort + num_sends)
192 verifyStats(self, egress_port, test_timeout, initTxOutPort + num_sends, initRxOutPort)
Shudong Zhou1d423922012-08-04 16:45:02 -0700193
Rich Laneb90a1c42012-10-05 09:16:05 -0700194class MultiFlowStats(base_tests.SimpleDataPlane):
Shudong Zhou1d423922012-08-04 16:45:02 -0700195 """
196 Verify flow stats are properly retrieved.
197
198 Generate two packets and install two matching flows
199 Send some number of packets
200 Send a port stats request to get packet count
201 Verify that the packet counter has incremented
202 """
203
204 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
Ed Swierk99a74de2012-08-22 06:40:54 -0700205 match = packet_to_flow_match(self, pkt)
Shudong Zhou1d423922012-08-04 16:45:02 -0700206 match.wildcards &= ~ofp.OFPFW_IN_PORT
207 self.assertTrue(match is not None,
208 "Could not generate flow match from pkt")
209 match.in_port = ingress_port
210
Rich Lane28fa9272013-03-08 16:00:25 -0800211 flow_mod_msg = ofp.message.flow_mod()
Shudong Zhou1d423922012-08-04 16:45:02 -0700212 flow_mod_msg.match = match
213 flow_mod_msg.cookie = random.randint(0,9007199254740992)
214 flow_mod_msg.buffer_id = 0xffffffff
215 flow_mod_msg.idle_timeout = 0
216 flow_mod_msg.hard_timeout = 0
Rich Lane9d3cc6b2013-03-08 16:33:08 -0800217 act = ofp.action.output()
Shudong Zhou1d423922012-08-04 16:45:02 -0700218 act.port = egress_port
Rich Lanee30455b2013-01-03 16:24:44 -0800219 flow_mod_msg.actions.add(act)
Shudong Zhou1d423922012-08-04 16:45:02 -0700220
Rich Lane9a003812012-10-04 17:17:59 -0700221 logging.info("Ingress " + str(ingress_port) +
Shudong Zhou1d423922012-08-04 16:45:02 -0700222 " to egress " + str(egress_port))
223
224 return flow_mod_msg
225
Shudong Zhou1d423922012-08-04 16:45:02 -0700226 def runTest(self):
Shudong Zhou1d423922012-08-04 16:45:02 -0700227 # TODO: set from command-line parameter
228 test_timeout = 60
229
Rich Lane477f4812012-10-04 22:49:00 -0700230 of_ports = config["port_map"].keys()
Shudong Zhou1d423922012-08-04 16:45:02 -0700231 of_ports.sort()
232 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
233 ingress_port = of_ports[0];
234 egress_port1 = of_ports[1];
235 egress_port2 = of_ports[2];
236
Rich Lane32bf9482013-01-03 17:26:30 -0800237 delete_all_flows(self.controller)
Shudong Zhou1d423922012-08-04 16:45:02 -0700238
239 pkt1 = simple_tcp_packet()
240 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
241
242 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
243 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
244
Rich Lane9a003812012-10-04 17:17:59 -0700245 logging.info("Inserting flow1")
Rich Lane5c3151c2013-01-03 17:15:41 -0800246 self.controller.message_send(flow_mod_msg1)
Rich Lane9a003812012-10-04 17:17:59 -0700247 logging.info("Inserting flow2")
Rich Lane5c3151c2013-01-03 17:15:41 -0800248 self.controller.message_send(flow_mod_msg2)
Rich Lane3a261d52013-01-03 17:45:08 -0800249 do_barrier(self.controller)
Shudong Zhou1d423922012-08-04 16:45:02 -0700250
Shudong Zhou50051c72012-08-06 16:53:46 -0700251 # get initial port stats count
252 initTxInPort, initRxInPort = getStats(self, ingress_port)
253 initTxOutPort1, initRxOutPort1 = getStats(self, egress_port1)
254 initTxOutPort2, initRxOutPort2 = getStats(self, egress_port2)
255
Shudong Zhou1d423922012-08-04 16:45:02 -0700256 num_pkt1s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700257 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700258 num_pkt2s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700259 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700260 for i in range(0,num_pkt1s):
261 sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
262 for i in range(0,num_pkt2s):
263 sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
Shudong Zhou50051c72012-08-06 16:53:46 -0700264
265 verifyStats(self, ingress_port, test_timeout,
266 initTxInPort, initRxInPort + num_pkt1s + num_pkt2s)
267 verifyStats(self, egress_port1, test_timeout,
268 initTxOutPort1 + num_pkt1s, initRxOutPort1)
269 verifyStats(self, egress_port2, test_timeout,
270 initTxOutPort2 + num_pkt2s, initRxOutPort2)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700271
272class AllPortStats(base_tests.SimpleDataPlane):
273 """
274 Verify all port stats are properly retrieved.
275
276 First, get stats from each port. Then get all port stats, verify
277 consistency with single port stats.
278 """
279
280 # TODO: This is copied from MultiFlowStats. Need to combine.
281 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
282 match = packet_to_flow_match(self, pkt)
283 match.wildcards &= ~ofp.OFPFW_IN_PORT
284 self.assertTrue(match is not None,
285 "Could not generate flow match from pkt")
286 match.in_port = ingress_port
287
Rich Lane28fa9272013-03-08 16:00:25 -0800288 flow_mod_msg = ofp.message.flow_mod()
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700289 flow_mod_msg.match = match
290 flow_mod_msg.cookie = random.randint(0,9007199254740992)
291 flow_mod_msg.buffer_id = 0xffffffff
292 flow_mod_msg.idle_timeout = 0
293 flow_mod_msg.hard_timeout = 0
Rich Lane9d3cc6b2013-03-08 16:33:08 -0800294 act = ofp.action.output()
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700295 act.port = egress_port
Rich Lanee30455b2013-01-03 16:24:44 -0800296 flow_mod_msg.actions.add(act)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700297
298 logging.info("Ingress " + str(ingress_port) +
299 " to egress " + str(egress_port))
300
301 return flow_mod_msg
302
303 def runTest(self):
304 # TODO: set from command-line parameter
305 test_timeout = 60
306
307 of_ports = config["port_map"].keys()
308 of_ports.sort()
309 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
310 port0 = of_ports[0];
311 port1 = of_ports[1];
312 port2 = of_ports[2];
313
314 # construct some packets and flows, send to switch
315 pkt1 = simple_tcp_packet()
316 flow_mod_msg1 = self.buildFlowModMsg(pkt1, port0, port1)
317
318 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
319 flow_mod_msg2 = self.buildFlowModMsg(pkt2, port0, port2)
320
321 logging.info("Inserting flow1")
Rich Lane5c3151c2013-01-03 17:15:41 -0800322 self.controller.message_send(flow_mod_msg1)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700323 logging.info("Inserting flow2")
Rich Lane5c3151c2013-01-03 17:15:41 -0800324 self.controller.message_send(flow_mod_msg2)
Rich Lane3a261d52013-01-03 17:45:08 -0800325 do_barrier(self.controller)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700326
327 num_pkt1s = random.randint(5,10)
328 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
329 num_pkt2s = random.randint(10,15)
330 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
331 for i in range(0,num_pkt1s):
332 sendPacket(self, pkt1, port0, port1, test_timeout)
333 for i in range(0,num_pkt2s):
334 sendPacket(self, pkt2, port0, port2, test_timeout)
335
336 # get individual port stats count
337 port_stats = {}
338 port_stats[ port0 ] = getStats(self, port0)
339 port_stats[ port1 ] = getStats(self, port1)
340 port_stats[ port2 ] = getStats(self, port2)
341
342 all_stats = getAllStats(self)
Rich Lanee53897c2012-10-30 09:40:13 -0700343 self.assertEqual(port_stats[ port0 ], all_stats[ port0 ])
344 self.assertEqual(port_stats[ port1 ], all_stats[ port1 ])
345 self.assertEqual(port_stats[ port2 ], all_stats[ port2 ])