blob: 8d39d6ff18533b47c71673b7946f59cbaed9f238 [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
14import oftest.cstruct as ofp
15import oftest.message as message
16import oftest.dataplane as dataplane
17import oftest.action as action
18import oftest.parse as parse
Rich Laneb90a1c42012-10-05 09:16:05 -070019import oftest.base_tests as base_tests
Shudong Zhoudf510a82012-08-03 18:08:40 -070020
Rich Laneda3b5ad2012-10-03 09:05:32 -070021from oftest.testutils import *
Shudong Zhoudf510a82012-08-03 18:08:40 -070022from time import sleep
23
Shudong Zhoudf510a82012-08-03 18:08:40 -070024# TODO: ovs has problems with VLAN id?
25WILDCARD_VALUES = [ofp.OFPFW_IN_PORT,
26 # (ofp.OFPFW_DL_VLAN | ofp.OFPFW_DL_VLAN_PCP),
27 ofp.OFPFW_DL_SRC,
28 ofp.OFPFW_DL_DST,
29 (ofp.OFPFW_DL_TYPE | ofp.OFPFW_NW_SRC_ALL |
30 ofp.OFPFW_NW_DST_ALL | ofp.OFPFW_NW_TOS | ofp.OFPFW_NW_PROTO |
31 ofp.OFPFW_TP_SRC | ofp.OFPFW_TP_DST),
32 (ofp.OFPFW_NW_PROTO | ofp.OFPFW_TP_SRC | ofp.OFPFW_TP_DST),
33 ofp.OFPFW_TP_SRC,
34 ofp.OFPFW_TP_DST,
35 ofp.OFPFW_NW_SRC_MASK,
36 ofp.OFPFW_NW_DST_MASK,
37 ofp.OFPFW_DL_VLAN_PCP,
38 ofp.OFPFW_NW_TOS]
39
Shudong Zhoudf510a82012-08-03 18:08:40 -070040def sendPacket(obj, pkt, ingress_port, egress_port, test_timeout):
41
Rich Lane9a003812012-10-04 17:17:59 -070042 logging.info("Sending packet to dp port " + str(ingress_port) +
Shudong Zhoudf510a82012-08-03 18:08:40 -070043 ", expecting output on " + str(egress_port))
44 obj.dataplane.send(ingress_port, str(pkt))
45
46 exp_pkt_arg = None
47 exp_port = None
Rich Lane477f4812012-10-04 22:49:00 -070048 if config["relax"]:
Shudong Zhoudf510a82012-08-03 18:08:40 -070049 exp_pkt_arg = pkt
50 exp_port = egress_port
51
52 (rcv_port, rcv_pkt, pkt_time) = obj.dataplane.poll(port_number=exp_port,
53 exp_pkt=exp_pkt_arg)
54 obj.assertTrue(rcv_pkt is not None,
55 "Packet not received on port " + str(egress_port))
Rich Lane9a003812012-10-04 17:17:59 -070056 logging.debug("Packet len " + str(len(rcv_pkt)) + " in on " +
Shudong Zhoudf510a82012-08-03 18:08:40 -070057 str(rcv_port))
58 obj.assertEqual(rcv_port, egress_port,
59 "Packet received on port " + str(rcv_port) +
60 ", expected port " + str(egress_port))
61 obj.assertEqual(str(pkt), str(rcv_pkt),
62 'Response packet does not match send packet')
63
Shudong Zhou50051c72012-08-06 16:53:46 -070064def getStats(obj, port):
65 stat_req = message.port_stats_request()
66 stat_req.port_no = port
67
Rich Lane9a003812012-10-04 17:17:59 -070068 logging.info("Sending stats request")
Shudong Zhou50051c72012-08-06 16:53:46 -070069 response, pkt = obj.controller.transact(stat_req, timeout=2)
70 obj.assertTrue(response is not None,
71 "No response to stats request")
72 obj.assertTrue(len(response.stats) == 1,
73 "Did not receive port stats reply")
74 for item in response.stats:
Rich Lane9a003812012-10-04 17:17:59 -070075 logging.info("Sent " + str(item.tx_packets) + " packets")
Shudong Zhou50051c72012-08-06 16:53:46 -070076 packet_sent = item.tx_packets
77 packet_recv = item.rx_packets
Rich Lane9a003812012-10-04 17:17:59 -070078 logging.info("Port %d stats count: tx %d rx %d" % (port, packet_sent, packet_recv))
Shudong Zhou50051c72012-08-06 16:53:46 -070079 return packet_sent, packet_recv
80
Shudong Zhou1a5b0822012-10-29 22:03:34 -070081def getAllStats(obj):
82 stat_req = message.port_stats_request()
83 stat_req.port_no = ofp.OFPP_NONE
84
85 logging.info("Sending all port stats request")
86 response, pkt = obj.controller.transact(stat_req, timeout=2)
87 obj.assertTrue(response is not None,
88 "No response to stats request")
89 obj.assertTrue(len(response.stats) >= 3,
90 "Did not receive all port stats reply")
91 stats = {}
92 for item in response.stats:
93 stats[ item.port_no ] = ( item.tx_packets, item.rx_packets )
94 return stats
95
Shudong Zhou50051c72012-08-06 16:53:46 -070096def verifyStats(obj, port, test_timeout, packet_sent, packet_recv):
97 stat_req = message.port_stats_request()
98 stat_req.port_no = port
99
100 all_packets_received = 0
101 all_packets_sent = 0
102 sent = recv = 0
103 for i in range(0,test_timeout):
Rich Lane9a003812012-10-04 17:17:59 -0700104 logging.info("Sending stats request")
Shudong Zhou50051c72012-08-06 16:53:46 -0700105 response, pkt = obj.controller.transact(stat_req,
106 timeout=test_timeout)
107 obj.assertTrue(response is not None,
108 "No response to stats request")
109 obj.assertTrue(len(response.stats) == 1,
110 "Did not receive port stats reply")
111 for item in response.stats:
112 sent = item.tx_packets
113 recv = item.rx_packets
Rich Lane9a003812012-10-04 17:17:59 -0700114 logging.info("Sent " + str(item.tx_packets) + " packets")
Shudong Zhou50051c72012-08-06 16:53:46 -0700115 if item.tx_packets == packet_sent:
116 all_packets_sent = 1
Rich Lane9a003812012-10-04 17:17:59 -0700117 logging.info("Received " + str(item.rx_packets) + " packets")
Shudong Zhou50051c72012-08-06 16:53:46 -0700118 if item.rx_packets == packet_recv:
119 all_packets_received = 1
120
121 if all_packets_received and all_packets_sent:
122 break
123 sleep(1)
124
Rich Lane9a003812012-10-04 17:17:59 -0700125 logging.info("Expected port %d stats count: tx %d rx %d" % (port, packet_sent, packet_recv))
126 logging.info("Actual port %d stats count: tx %d rx %d" % (port, sent, recv))
Shudong Zhou50051c72012-08-06 16:53:46 -0700127 obj.assertTrue(all_packets_sent,
128 "Packet sent does not match number sent")
129 obj.assertTrue(all_packets_received,
130 "Packet received does not match number sent")
131
Rich Lane97e99652013-01-02 17:23:20 -0800132@group('smoke')
Rich Laneb90a1c42012-10-05 09:16:05 -0700133class SingleFlowStats(base_tests.SimpleDataPlane):
Shudong Zhoudf510a82012-08-03 18:08:40 -0700134 """
135 Verify flow stats are properly retrieved.
136
137 Generate a packet
Shudong Zhou1d423922012-08-04 16:45:02 -0700138 Generate and install a flow from port 1 to 2
Shudong Zhoudf510a82012-08-03 18:08:40 -0700139 Send the packet
Shudong Zhou1d423922012-08-04 16:45:02 -0700140 Send port stats request to port 1 & 2
Shudong Zhoudf510a82012-08-03 18:08:40 -0700141 Verify that the packet counter has incremented
142 """
143
Shudong Zhoudf510a82012-08-03 18:08:40 -0700144 def runTest(self):
Shudong Zhoudf510a82012-08-03 18:08:40 -0700145 # TODO: set from command-line parameter
146 test_timeout = 60
147
Rich Lane477f4812012-10-04 22:49:00 -0700148 of_ports = config["port_map"].keys()
Shudong Zhoudf510a82012-08-03 18:08:40 -0700149 of_ports.sort()
150 self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
151
Rich Lane9a003812012-10-04 17:17:59 -0700152 rc = delete_all_flows(self.controller)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700153 self.assertEqual(rc, 0, "Failed to delete all flows")
154
155 # build packet
156 pkt = simple_tcp_packet()
Ed Swierk99a74de2012-08-22 06:40:54 -0700157 match = packet_to_flow_match(self, pkt)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700158 match.wildcards &= ~ofp.OFPFW_IN_PORT
159 self.assertTrue(match is not None,
160 "Could not generate flow match from pkt")
161 act = action.action_output()
162
163 # build flow
164 ingress_port = of_ports[0];
165 egress_port = of_ports[1];
Rich Lane9a003812012-10-04 17:17:59 -0700166 logging.info("Ingress " + str(ingress_port) +
Shudong Zhoudf510a82012-08-03 18:08:40 -0700167 " to egress " + str(egress_port))
168 match.in_port = ingress_port
169 flow_mod_msg = message.flow_mod()
170 flow_mod_msg.match = match
171 flow_mod_msg.cookie = random.randint(0,9007199254740992)
172 flow_mod_msg.buffer_id = 0xffffffff
173 flow_mod_msg.idle_timeout = 0
174 flow_mod_msg.hard_timeout = 0
175 act.port = egress_port
176 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
177
178 # send flow
Rich Lane9a003812012-10-04 17:17:59 -0700179 logging.info("Inserting flow")
Shudong Zhoudf510a82012-08-03 18:08:40 -0700180 rv = self.controller.message_send(flow_mod_msg)
181 self.assertTrue(rv != -1, "Error installing flow mod")
182 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
183
Shudong Zhou50051c72012-08-06 16:53:46 -0700184 # get initial port stats count
185 initTxInPort, initRxInPort = getStats(self, ingress_port)
186 initTxOutPort, initRxOutPort = getStats(self, egress_port)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700187
188 # send packet N times
189 num_sends = random.randint(10,20)
Rich Lane9a003812012-10-04 17:17:59 -0700190 logging.info("Sending " + str(num_sends) + " test packets")
Shudong Zhoudf510a82012-08-03 18:08:40 -0700191 for i in range(0,num_sends):
192 sendPacket(self, pkt, ingress_port, egress_port,
193 test_timeout)
194
Shudong Zhou50051c72012-08-06 16:53:46 -0700195 verifyStats(self, ingress_port, test_timeout, initTxInPort, initRxInPort + num_sends)
196 verifyStats(self, egress_port, test_timeout, initTxOutPort + num_sends, initRxOutPort)
Shudong Zhou1d423922012-08-04 16:45:02 -0700197
Rich Laneb90a1c42012-10-05 09:16:05 -0700198class MultiFlowStats(base_tests.SimpleDataPlane):
Shudong Zhou1d423922012-08-04 16:45:02 -0700199 """
200 Verify flow stats are properly retrieved.
201
202 Generate two packets and install two matching flows
203 Send some number of packets
204 Send a port stats request to get packet count
205 Verify that the packet counter has incremented
206 """
207
208 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
Ed Swierk99a74de2012-08-22 06:40:54 -0700209 match = packet_to_flow_match(self, pkt)
Shudong Zhou1d423922012-08-04 16:45:02 -0700210 match.wildcards &= ~ofp.OFPFW_IN_PORT
211 self.assertTrue(match is not None,
212 "Could not generate flow match from pkt")
213 match.in_port = ingress_port
214
215 flow_mod_msg = message.flow_mod()
216 flow_mod_msg.match = match
217 flow_mod_msg.cookie = random.randint(0,9007199254740992)
218 flow_mod_msg.buffer_id = 0xffffffff
219 flow_mod_msg.idle_timeout = 0
220 flow_mod_msg.hard_timeout = 0
221 act = action.action_output()
222 act.port = egress_port
223 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
224
Rich Lane9a003812012-10-04 17:17:59 -0700225 logging.info("Ingress " + str(ingress_port) +
Shudong Zhou1d423922012-08-04 16:45:02 -0700226 " to egress " + str(egress_port))
227
228 return flow_mod_msg
229
Shudong Zhou1d423922012-08-04 16:45:02 -0700230 def runTest(self):
Shudong Zhou1d423922012-08-04 16:45:02 -0700231 # TODO: set from command-line parameter
232 test_timeout = 60
233
Rich Lane477f4812012-10-04 22:49:00 -0700234 of_ports = config["port_map"].keys()
Shudong Zhou1d423922012-08-04 16:45:02 -0700235 of_ports.sort()
236 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
237 ingress_port = of_ports[0];
238 egress_port1 = of_ports[1];
239 egress_port2 = of_ports[2];
240
Rich Lane9a003812012-10-04 17:17:59 -0700241 rc = delete_all_flows(self.controller)
Shudong Zhou1d423922012-08-04 16:45:02 -0700242 self.assertEqual(rc, 0, "Failed to delete all flows")
243
244 pkt1 = simple_tcp_packet()
245 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
246
247 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
248 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
249
Rich Lane9a003812012-10-04 17:17:59 -0700250 logging.info("Inserting flow1")
Shudong Zhou1d423922012-08-04 16:45:02 -0700251 rv = self.controller.message_send(flow_mod_msg1)
252 self.assertTrue(rv != -1, "Error installing flow mod")
Rich Lane9a003812012-10-04 17:17:59 -0700253 logging.info("Inserting flow2")
Shudong Zhou1d423922012-08-04 16:45:02 -0700254 rv = self.controller.message_send(flow_mod_msg2)
255 self.assertTrue(rv != -1, "Error installing flow mod")
256 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
257
Shudong Zhou50051c72012-08-06 16:53:46 -0700258 # get initial port stats count
259 initTxInPort, initRxInPort = getStats(self, ingress_port)
260 initTxOutPort1, initRxOutPort1 = getStats(self, egress_port1)
261 initTxOutPort2, initRxOutPort2 = getStats(self, egress_port2)
262
Shudong Zhou1d423922012-08-04 16:45:02 -0700263 num_pkt1s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700264 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700265 num_pkt2s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700266 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700267 for i in range(0,num_pkt1s):
268 sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
269 for i in range(0,num_pkt2s):
270 sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
Shudong Zhou50051c72012-08-06 16:53:46 -0700271
272 verifyStats(self, ingress_port, test_timeout,
273 initTxInPort, initRxInPort + num_pkt1s + num_pkt2s)
274 verifyStats(self, egress_port1, test_timeout,
275 initTxOutPort1 + num_pkt1s, initRxOutPort1)
276 verifyStats(self, egress_port2, test_timeout,
277 initTxOutPort2 + num_pkt2s, initRxOutPort2)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700278
279class AllPortStats(base_tests.SimpleDataPlane):
280 """
281 Verify all port stats are properly retrieved.
282
283 First, get stats from each port. Then get all port stats, verify
284 consistency with single port stats.
285 """
286
287 # TODO: This is copied from MultiFlowStats. Need to combine.
288 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
289 match = packet_to_flow_match(self, pkt)
290 match.wildcards &= ~ofp.OFPFW_IN_PORT
291 self.assertTrue(match is not None,
292 "Could not generate flow match from pkt")
293 match.in_port = ingress_port
294
295 flow_mod_msg = message.flow_mod()
296 flow_mod_msg.match = match
297 flow_mod_msg.cookie = random.randint(0,9007199254740992)
298 flow_mod_msg.buffer_id = 0xffffffff
299 flow_mod_msg.idle_timeout = 0
300 flow_mod_msg.hard_timeout = 0
301 act = action.action_output()
302 act.port = egress_port
303 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
304
305 logging.info("Ingress " + str(ingress_port) +
306 " to egress " + str(egress_port))
307
308 return flow_mod_msg
309
310 def runTest(self):
311 # TODO: set from command-line parameter
312 test_timeout = 60
313
314 of_ports = config["port_map"].keys()
315 of_ports.sort()
316 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
317 port0 = of_ports[0];
318 port1 = of_ports[1];
319 port2 = of_ports[2];
320
321 # construct some packets and flows, send to switch
322 pkt1 = simple_tcp_packet()
323 flow_mod_msg1 = self.buildFlowModMsg(pkt1, port0, port1)
324
325 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
326 flow_mod_msg2 = self.buildFlowModMsg(pkt2, port0, port2)
327
328 logging.info("Inserting flow1")
329 rv = self.controller.message_send(flow_mod_msg1)
330 self.assertTrue(rv != -1, "Error installing flow mod")
331 logging.info("Inserting flow2")
332 rv = self.controller.message_send(flow_mod_msg2)
333 self.assertTrue(rv != -1, "Error installing flow mod")
334 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
335
336 num_pkt1s = random.randint(5,10)
337 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
338 num_pkt2s = random.randint(10,15)
339 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
340 for i in range(0,num_pkt1s):
341 sendPacket(self, pkt1, port0, port1, test_timeout)
342 for i in range(0,num_pkt2s):
343 sendPacket(self, pkt2, port0, port2, test_timeout)
344
345 # get individual port stats count
346 port_stats = {}
347 port_stats[ port0 ] = getStats(self, port0)
348 port_stats[ port1 ] = getStats(self, port1)
349 port_stats[ port2 ] = getStats(self, port2)
350
351 all_stats = getAllStats(self)
Rich Lanee53897c2012-10-30 09:40:13 -0700352 self.assertEqual(port_stats[ port0 ], all_stats[ port0 ])
353 self.assertEqual(port_stats[ port1 ], all_stats[ port1 ])
354 self.assertEqual(port_stats[ port2 ], all_stats[ port2 ])