blob: 41bc0cfecc67226a9616a300a7e8ce2654304711 [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 Lane0d4a5582015-04-10 12:25:39 -070063 entries = get_port_stats(obj, port)
64 for item in entries:
Rich Lane9a003812012-10-04 17:17:59 -070065 logging.info("Sent " + str(item.tx_packets) + " packets")
Shudong Zhou50051c72012-08-06 16:53:46 -070066 packet_sent = item.tx_packets
67 packet_recv = item.rx_packets
Rich Lane9a003812012-10-04 17:17:59 -070068 logging.info("Port %d stats count: tx %d rx %d" % (port, packet_sent, packet_recv))
Shudong Zhou50051c72012-08-06 16:53:46 -070069 return packet_sent, packet_recv
70
Shudong Zhou1a5b0822012-10-29 22:03:34 -070071def getAllStats(obj):
Rich Lane0d4a5582015-04-10 12:25:39 -070072 entries = get_port_stats(obj, ofp.OFPP_NONE)
Shudong Zhou1a5b0822012-10-29 22:03:34 -070073 stats = {}
Rich Lane0d4a5582015-04-10 12:25:39 -070074 for item in entries:
Shudong Zhou1a5b0822012-10-29 22:03:34 -070075 stats[ item.port_no ] = ( item.tx_packets, item.rx_packets )
76 return stats
77
Shudong Zhou50051c72012-08-06 16:53:46 -070078def verifyStats(obj, port, test_timeout, packet_sent, packet_recv):
Rich Lane28fa9272013-03-08 16:00:25 -080079 stat_req = ofp.message.port_stats_request()
Shudong Zhou50051c72012-08-06 16:53:46 -070080 stat_req.port_no = port
81
82 all_packets_received = 0
83 all_packets_sent = 0
84 sent = recv = 0
85 for i in range(0,test_timeout):
Rich Lane9a003812012-10-04 17:17:59 -070086 logging.info("Sending stats request")
Shudong Zhou50051c72012-08-06 16:53:46 -070087 response, pkt = obj.controller.transact(stat_req,
88 timeout=test_timeout)
89 obj.assertTrue(response is not None,
90 "No response to stats request")
Rich Lane5fd6faf2013-03-11 13:30:20 -070091 obj.assertTrue(len(response.entries) == 1,
Shudong Zhou50051c72012-08-06 16:53:46 -070092 "Did not receive port stats reply")
Rich Lane5fd6faf2013-03-11 13:30:20 -070093 for item in response.entries:
Shudong Zhou50051c72012-08-06 16:53:46 -070094 sent = item.tx_packets
95 recv = item.rx_packets
Rich Lane9a003812012-10-04 17:17:59 -070096 logging.info("Sent " + str(item.tx_packets) + " packets")
Rich Lane6d618172015-04-10 12:22:55 -070097 if item.tx_packets >= packet_sent:
Shudong Zhou50051c72012-08-06 16:53:46 -070098 all_packets_sent = 1
Rich Lane9a003812012-10-04 17:17:59 -070099 logging.info("Received " + str(item.rx_packets) + " packets")
Rich Lane6d618172015-04-10 12:22:55 -0700100 if item.rx_packets >= packet_recv:
Shudong Zhou50051c72012-08-06 16:53:46 -0700101 all_packets_received = 1
102
103 if all_packets_received and all_packets_sent:
104 break
105 sleep(1)
106
Rich Lane9a003812012-10-04 17:17:59 -0700107 logging.info("Expected port %d stats count: tx %d rx %d" % (port, packet_sent, packet_recv))
108 logging.info("Actual port %d stats count: tx %d rx %d" % (port, sent, recv))
Shudong Zhou50051c72012-08-06 16:53:46 -0700109 obj.assertTrue(all_packets_sent,
110 "Packet sent does not match number sent")
111 obj.assertTrue(all_packets_received,
112 "Packet received does not match number sent")
113
Rich Lane97e99652013-01-02 17:23:20 -0800114@group('smoke')
Rich Laneb90a1c42012-10-05 09:16:05 -0700115class SingleFlowStats(base_tests.SimpleDataPlane):
Shudong Zhoudf510a82012-08-03 18:08:40 -0700116 """
117 Verify flow stats are properly retrieved.
118
119 Generate a packet
Shudong Zhou1d423922012-08-04 16:45:02 -0700120 Generate and install a flow from port 1 to 2
Shudong Zhoudf510a82012-08-03 18:08:40 -0700121 Send the packet
Shudong Zhou1d423922012-08-04 16:45:02 -0700122 Send port stats request to port 1 & 2
Shudong Zhoudf510a82012-08-03 18:08:40 -0700123 Verify that the packet counter has incremented
124 """
125
Shudong Zhoudf510a82012-08-03 18:08:40 -0700126 def runTest(self):
Shudong Zhoudf510a82012-08-03 18:08:40 -0700127 # TODO: set from command-line parameter
128 test_timeout = 60
129
Rich Lane477f4812012-10-04 22:49:00 -0700130 of_ports = config["port_map"].keys()
Shudong Zhoudf510a82012-08-03 18:08:40 -0700131 of_ports.sort()
132 self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
133
Rich Lane32bf9482013-01-03 17:26:30 -0800134 delete_all_flows(self.controller)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700135
136 # build packet
137 pkt = simple_tcp_packet()
Ed Swierk99a74de2012-08-22 06:40:54 -0700138 match = packet_to_flow_match(self, pkt)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700139 match.wildcards &= ~ofp.OFPFW_IN_PORT
140 self.assertTrue(match is not None,
141 "Could not generate flow match from pkt")
Rich Lane9d3cc6b2013-03-08 16:33:08 -0800142 act = ofp.action.output()
Shudong Zhoudf510a82012-08-03 18:08:40 -0700143
144 # build flow
145 ingress_port = of_ports[0];
146 egress_port = of_ports[1];
Rich Lane9a003812012-10-04 17:17:59 -0700147 logging.info("Ingress " + str(ingress_port) +
Shudong Zhoudf510a82012-08-03 18:08:40 -0700148 " to egress " + str(egress_port))
149 match.in_port = ingress_port
Rich Laneba3f0e22013-03-11 16:43:57 -0700150 flow_mod_msg = ofp.message.flow_add()
Shudong Zhoudf510a82012-08-03 18:08:40 -0700151 flow_mod_msg.match = match
152 flow_mod_msg.cookie = random.randint(0,9007199254740992)
153 flow_mod_msg.buffer_id = 0xffffffff
154 flow_mod_msg.idle_timeout = 0
155 flow_mod_msg.hard_timeout = 0
156 act.port = egress_port
Rich Lanec495d9e2013-03-08 17:43:36 -0800157 flow_mod_msg.actions.append(act)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700158
159 # send flow
Rich Lane9a003812012-10-04 17:17:59 -0700160 logging.info("Inserting flow")
Rich Lane5c3151c2013-01-03 17:15:41 -0800161 self.controller.message_send(flow_mod_msg)
Rich Lane3a261d52013-01-03 17:45:08 -0800162 do_barrier(self.controller)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700163
Shudong Zhou50051c72012-08-06 16:53:46 -0700164 # get initial port stats count
165 initTxInPort, initRxInPort = getStats(self, ingress_port)
166 initTxOutPort, initRxOutPort = getStats(self, egress_port)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700167
168 # send packet N times
169 num_sends = random.randint(10,20)
Rich Lane9a003812012-10-04 17:17:59 -0700170 logging.info("Sending " + str(num_sends) + " test packets")
Shudong Zhoudf510a82012-08-03 18:08:40 -0700171 for i in range(0,num_sends):
172 sendPacket(self, pkt, ingress_port, egress_port,
173 test_timeout)
174
Shudong Zhou50051c72012-08-06 16:53:46 -0700175 verifyStats(self, ingress_port, test_timeout, initTxInPort, initRxInPort + num_sends)
176 verifyStats(self, egress_port, test_timeout, initTxOutPort + num_sends, initRxOutPort)
Shudong Zhou1d423922012-08-04 16:45:02 -0700177
Rich Laneb90a1c42012-10-05 09:16:05 -0700178class MultiFlowStats(base_tests.SimpleDataPlane):
Shudong Zhou1d423922012-08-04 16:45:02 -0700179 """
180 Verify flow stats are properly retrieved.
181
182 Generate two packets and install two matching flows
183 Send some number of packets
184 Send a port stats request to get packet count
185 Verify that the packet counter has incremented
186 """
187
188 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
Ed Swierk99a74de2012-08-22 06:40:54 -0700189 match = packet_to_flow_match(self, pkt)
Shudong Zhou1d423922012-08-04 16:45:02 -0700190 match.wildcards &= ~ofp.OFPFW_IN_PORT
191 self.assertTrue(match is not None,
192 "Could not generate flow match from pkt")
193 match.in_port = ingress_port
194
Rich Laneba3f0e22013-03-11 16:43:57 -0700195 flow_mod_msg = ofp.message.flow_add()
Shudong Zhou1d423922012-08-04 16:45:02 -0700196 flow_mod_msg.match = match
197 flow_mod_msg.cookie = random.randint(0,9007199254740992)
198 flow_mod_msg.buffer_id = 0xffffffff
199 flow_mod_msg.idle_timeout = 0
200 flow_mod_msg.hard_timeout = 0
Rich Lane9d3cc6b2013-03-08 16:33:08 -0800201 act = ofp.action.output()
Shudong Zhou1d423922012-08-04 16:45:02 -0700202 act.port = egress_port
Rich Lanec495d9e2013-03-08 17:43:36 -0800203 flow_mod_msg.actions.append(act)
Shudong Zhou1d423922012-08-04 16:45:02 -0700204
Rich Lane9a003812012-10-04 17:17:59 -0700205 logging.info("Ingress " + str(ingress_port) +
Shudong Zhou1d423922012-08-04 16:45:02 -0700206 " to egress " + str(egress_port))
207
208 return flow_mod_msg
209
Shudong Zhou1d423922012-08-04 16:45:02 -0700210 def runTest(self):
Shudong Zhou1d423922012-08-04 16:45:02 -0700211 # TODO: set from command-line parameter
212 test_timeout = 60
213
Rich Lane477f4812012-10-04 22:49:00 -0700214 of_ports = config["port_map"].keys()
Shudong Zhou1d423922012-08-04 16:45:02 -0700215 of_ports.sort()
216 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
217 ingress_port = of_ports[0];
218 egress_port1 = of_ports[1];
219 egress_port2 = of_ports[2];
220
Rich Lane32bf9482013-01-03 17:26:30 -0800221 delete_all_flows(self.controller)
Shudong Zhou1d423922012-08-04 16:45:02 -0700222
223 pkt1 = simple_tcp_packet()
224 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
225
Rich Laned0478ff2013-03-11 12:46:58 -0700226 pkt2 = simple_tcp_packet(eth_src='0:7:7:7:7:7')
Shudong Zhou1d423922012-08-04 16:45:02 -0700227 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
228
Rich Lane9a003812012-10-04 17:17:59 -0700229 logging.info("Inserting flow1")
Rich Lane5c3151c2013-01-03 17:15:41 -0800230 self.controller.message_send(flow_mod_msg1)
Rich Lane9a003812012-10-04 17:17:59 -0700231 logging.info("Inserting flow2")
Rich Lane5c3151c2013-01-03 17:15:41 -0800232 self.controller.message_send(flow_mod_msg2)
Rich Lane3a261d52013-01-03 17:45:08 -0800233 do_barrier(self.controller)
Shudong Zhou1d423922012-08-04 16:45:02 -0700234
Shudong Zhou50051c72012-08-06 16:53:46 -0700235 # get initial port stats count
236 initTxInPort, initRxInPort = getStats(self, ingress_port)
237 initTxOutPort1, initRxOutPort1 = getStats(self, egress_port1)
238 initTxOutPort2, initRxOutPort2 = getStats(self, egress_port2)
239
Shudong Zhou1d423922012-08-04 16:45:02 -0700240 num_pkt1s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700241 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700242 num_pkt2s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700243 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700244 for i in range(0,num_pkt1s):
245 sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
246 for i in range(0,num_pkt2s):
247 sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
Shudong Zhou50051c72012-08-06 16:53:46 -0700248
249 verifyStats(self, ingress_port, test_timeout,
250 initTxInPort, initRxInPort + num_pkt1s + num_pkt2s)
251 verifyStats(self, egress_port1, test_timeout,
252 initTxOutPort1 + num_pkt1s, initRxOutPort1)
253 verifyStats(self, egress_port2, test_timeout,
254 initTxOutPort2 + num_pkt2s, initRxOutPort2)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700255
256class AllPortStats(base_tests.SimpleDataPlane):
257 """
258 Verify all port stats are properly retrieved.
259
260 First, get stats from each port. Then get all port stats, verify
261 consistency with single port stats.
262 """
263
264 # TODO: This is copied from MultiFlowStats. Need to combine.
265 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
266 match = packet_to_flow_match(self, pkt)
267 match.wildcards &= ~ofp.OFPFW_IN_PORT
268 self.assertTrue(match is not None,
269 "Could not generate flow match from pkt")
270 match.in_port = ingress_port
271
Rich Laneba3f0e22013-03-11 16:43:57 -0700272 flow_mod_msg = ofp.message.flow_add()
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700273 flow_mod_msg.match = match
274 flow_mod_msg.cookie = random.randint(0,9007199254740992)
275 flow_mod_msg.buffer_id = 0xffffffff
276 flow_mod_msg.idle_timeout = 0
277 flow_mod_msg.hard_timeout = 0
Rich Lane9d3cc6b2013-03-08 16:33:08 -0800278 act = ofp.action.output()
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700279 act.port = egress_port
Rich Lanec495d9e2013-03-08 17:43:36 -0800280 flow_mod_msg.actions.append(act)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700281
282 logging.info("Ingress " + str(ingress_port) +
283 " to egress " + str(egress_port))
284
285 return flow_mod_msg
286
287 def runTest(self):
Rich Laneae68aec2015-04-08 13:58:34 -0700288 delete_all_flows(self.controller)
289 do_barrier(self.controller)
290
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700291 # TODO: set from command-line parameter
292 test_timeout = 60
293
294 of_ports = config["port_map"].keys()
295 of_ports.sort()
296 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
297 port0 = of_ports[0];
298 port1 = of_ports[1];
299 port2 = of_ports[2];
300
301 # construct some packets and flows, send to switch
302 pkt1 = simple_tcp_packet()
303 flow_mod_msg1 = self.buildFlowModMsg(pkt1, port0, port1)
304
Rich Laned0478ff2013-03-11 12:46:58 -0700305 pkt2 = simple_tcp_packet(eth_src='0:7:7:7:7:7')
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700306 flow_mod_msg2 = self.buildFlowModMsg(pkt2, port0, port2)
307
308 logging.info("Inserting flow1")
Rich Lane5c3151c2013-01-03 17:15:41 -0800309 self.controller.message_send(flow_mod_msg1)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700310 logging.info("Inserting flow2")
Rich Lane5c3151c2013-01-03 17:15:41 -0800311 self.controller.message_send(flow_mod_msg2)
Rich Lane3a261d52013-01-03 17:45:08 -0800312 do_barrier(self.controller)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700313
314 num_pkt1s = random.randint(5,10)
315 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
316 num_pkt2s = random.randint(10,15)
317 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
318 for i in range(0,num_pkt1s):
319 sendPacket(self, pkt1, port0, port1, test_timeout)
320 for i in range(0,num_pkt2s):
321 sendPacket(self, pkt2, port0, port2, test_timeout)
322
323 # get individual port stats count
324 port_stats = {}
325 port_stats[ port0 ] = getStats(self, port0)
326 port_stats[ port1 ] = getStats(self, port1)
327 port_stats[ port2 ] = getStats(self, port2)
328
329 all_stats = getAllStats(self)
Rich Lanee53897c2012-10-30 09:40:13 -0700330 self.assertEqual(port_stats[ port0 ], all_stats[ port0 ])
331 self.assertEqual(port_stats[ port1 ], all_stats[ port1 ])
332 self.assertEqual(port_stats[ port2 ], all_stats[ port2 ])