blob: bacf1d84a227915b87d85de68ecbba32f9a430ba [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 Laneb90a1c42012-10-05 09:16:05 -0700132class SingleFlowStats(base_tests.SimpleDataPlane):
Shudong Zhoudf510a82012-08-03 18:08:40 -0700133 """
134 Verify flow stats are properly retrieved.
135
136 Generate a packet
Shudong Zhou1d423922012-08-04 16:45:02 -0700137 Generate and install a flow from port 1 to 2
Shudong Zhoudf510a82012-08-03 18:08:40 -0700138 Send the packet
Shudong Zhou1d423922012-08-04 16:45:02 -0700139 Send port stats request to port 1 & 2
Shudong Zhoudf510a82012-08-03 18:08:40 -0700140 Verify that the packet counter has incremented
141 """
142
Shudong Zhoudf510a82012-08-03 18:08:40 -0700143 def runTest(self):
Shudong Zhoudf510a82012-08-03 18:08:40 -0700144 # TODO: set from command-line parameter
145 test_timeout = 60
146
Rich Lane477f4812012-10-04 22:49:00 -0700147 of_ports = config["port_map"].keys()
Shudong Zhoudf510a82012-08-03 18:08:40 -0700148 of_ports.sort()
149 self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
150
Rich Lane9a003812012-10-04 17:17:59 -0700151 rc = delete_all_flows(self.controller)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700152 self.assertEqual(rc, 0, "Failed to delete all flows")
153
154 # build packet
155 pkt = simple_tcp_packet()
Ed Swierk99a74de2012-08-22 06:40:54 -0700156 match = packet_to_flow_match(self, pkt)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700157 match.wildcards &= ~ofp.OFPFW_IN_PORT
158 self.assertTrue(match is not None,
159 "Could not generate flow match from pkt")
160 act = action.action_output()
161
162 # build flow
163 ingress_port = of_ports[0];
164 egress_port = of_ports[1];
Rich Lane9a003812012-10-04 17:17:59 -0700165 logging.info("Ingress " + str(ingress_port) +
Shudong Zhoudf510a82012-08-03 18:08:40 -0700166 " to egress " + str(egress_port))
167 match.in_port = ingress_port
168 flow_mod_msg = message.flow_mod()
169 flow_mod_msg.match = match
170 flow_mod_msg.cookie = random.randint(0,9007199254740992)
171 flow_mod_msg.buffer_id = 0xffffffff
172 flow_mod_msg.idle_timeout = 0
173 flow_mod_msg.hard_timeout = 0
174 act.port = egress_port
Rich Lanee30455b2013-01-03 16:24:44 -0800175 flow_mod_msg.actions.add(act)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700176
177 # send flow
Rich Lane9a003812012-10-04 17:17:59 -0700178 logging.info("Inserting flow")
Rich Lane5c3151c2013-01-03 17:15:41 -0800179 self.controller.message_send(flow_mod_msg)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700180 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
181
Shudong Zhou50051c72012-08-06 16:53:46 -0700182 # get initial port stats count
183 initTxInPort, initRxInPort = getStats(self, ingress_port)
184 initTxOutPort, initRxOutPort = getStats(self, egress_port)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700185
186 # send packet N times
187 num_sends = random.randint(10,20)
Rich Lane9a003812012-10-04 17:17:59 -0700188 logging.info("Sending " + str(num_sends) + " test packets")
Shudong Zhoudf510a82012-08-03 18:08:40 -0700189 for i in range(0,num_sends):
190 sendPacket(self, pkt, ingress_port, egress_port,
191 test_timeout)
192
Shudong Zhou50051c72012-08-06 16:53:46 -0700193 verifyStats(self, ingress_port, test_timeout, initTxInPort, initRxInPort + num_sends)
194 verifyStats(self, egress_port, test_timeout, initTxOutPort + num_sends, initRxOutPort)
Shudong Zhou1d423922012-08-04 16:45:02 -0700195
Rich Laneb90a1c42012-10-05 09:16:05 -0700196class MultiFlowStats(base_tests.SimpleDataPlane):
Shudong Zhou1d423922012-08-04 16:45:02 -0700197 """
198 Verify flow stats are properly retrieved.
199
200 Generate two packets and install two matching flows
201 Send some number of packets
202 Send a port stats request to get packet count
203 Verify that the packet counter has incremented
204 """
205
206 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
Ed Swierk99a74de2012-08-22 06:40:54 -0700207 match = packet_to_flow_match(self, pkt)
Shudong Zhou1d423922012-08-04 16:45:02 -0700208 match.wildcards &= ~ofp.OFPFW_IN_PORT
209 self.assertTrue(match is not None,
210 "Could not generate flow match from pkt")
211 match.in_port = ingress_port
212
213 flow_mod_msg = message.flow_mod()
214 flow_mod_msg.match = match
215 flow_mod_msg.cookie = random.randint(0,9007199254740992)
216 flow_mod_msg.buffer_id = 0xffffffff
217 flow_mod_msg.idle_timeout = 0
218 flow_mod_msg.hard_timeout = 0
219 act = action.action_output()
220 act.port = egress_port
Rich Lanee30455b2013-01-03 16:24:44 -0800221 flow_mod_msg.actions.add(act)
Shudong Zhou1d423922012-08-04 16:45:02 -0700222
Rich Lane9a003812012-10-04 17:17:59 -0700223 logging.info("Ingress " + str(ingress_port) +
Shudong Zhou1d423922012-08-04 16:45:02 -0700224 " to egress " + str(egress_port))
225
226 return flow_mod_msg
227
Shudong Zhou1d423922012-08-04 16:45:02 -0700228 def runTest(self):
Shudong Zhou1d423922012-08-04 16:45:02 -0700229 # TODO: set from command-line parameter
230 test_timeout = 60
231
Rich Lane477f4812012-10-04 22:49:00 -0700232 of_ports = config["port_map"].keys()
Shudong Zhou1d423922012-08-04 16:45:02 -0700233 of_ports.sort()
234 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
235 ingress_port = of_ports[0];
236 egress_port1 = of_ports[1];
237 egress_port2 = of_ports[2];
238
Rich Lane9a003812012-10-04 17:17:59 -0700239 rc = delete_all_flows(self.controller)
Shudong Zhou1d423922012-08-04 16:45:02 -0700240 self.assertEqual(rc, 0, "Failed to delete all flows")
241
242 pkt1 = simple_tcp_packet()
243 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
244
245 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
246 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
247
Rich Lane9a003812012-10-04 17:17:59 -0700248 logging.info("Inserting flow1")
Rich Lane5c3151c2013-01-03 17:15:41 -0800249 self.controller.message_send(flow_mod_msg1)
Rich Lane9a003812012-10-04 17:17:59 -0700250 logging.info("Inserting flow2")
Rich Lane5c3151c2013-01-03 17:15:41 -0800251 self.controller.message_send(flow_mod_msg2)
Shudong Zhou1d423922012-08-04 16:45:02 -0700252 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
253
Shudong Zhou50051c72012-08-06 16:53:46 -0700254 # get initial port stats count
255 initTxInPort, initRxInPort = getStats(self, ingress_port)
256 initTxOutPort1, initRxOutPort1 = getStats(self, egress_port1)
257 initTxOutPort2, initRxOutPort2 = getStats(self, egress_port2)
258
Shudong Zhou1d423922012-08-04 16:45:02 -0700259 num_pkt1s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700260 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700261 num_pkt2s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700262 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700263 for i in range(0,num_pkt1s):
264 sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
265 for i in range(0,num_pkt2s):
266 sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
Shudong Zhou50051c72012-08-06 16:53:46 -0700267
268 verifyStats(self, ingress_port, test_timeout,
269 initTxInPort, initRxInPort + num_pkt1s + num_pkt2s)
270 verifyStats(self, egress_port1, test_timeout,
271 initTxOutPort1 + num_pkt1s, initRxOutPort1)
272 verifyStats(self, egress_port2, test_timeout,
273 initTxOutPort2 + num_pkt2s, initRxOutPort2)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700274
275class AllPortStats(base_tests.SimpleDataPlane):
276 """
277 Verify all port stats are properly retrieved.
278
279 First, get stats from each port. Then get all port stats, verify
280 consistency with single port stats.
281 """
282
283 # TODO: This is copied from MultiFlowStats. Need to combine.
284 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
285 match = packet_to_flow_match(self, pkt)
286 match.wildcards &= ~ofp.OFPFW_IN_PORT
287 self.assertTrue(match is not None,
288 "Could not generate flow match from pkt")
289 match.in_port = ingress_port
290
291 flow_mod_msg = message.flow_mod()
292 flow_mod_msg.match = match
293 flow_mod_msg.cookie = random.randint(0,9007199254740992)
294 flow_mod_msg.buffer_id = 0xffffffff
295 flow_mod_msg.idle_timeout = 0
296 flow_mod_msg.hard_timeout = 0
297 act = action.action_output()
298 act.port = egress_port
Rich Lanee30455b2013-01-03 16:24:44 -0800299 flow_mod_msg.actions.add(act)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700300
301 logging.info("Ingress " + str(ingress_port) +
302 " to egress " + str(egress_port))
303
304 return flow_mod_msg
305
306 def runTest(self):
307 # TODO: set from command-line parameter
308 test_timeout = 60
309
310 of_ports = config["port_map"].keys()
311 of_ports.sort()
312 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
313 port0 = of_ports[0];
314 port1 = of_ports[1];
315 port2 = of_ports[2];
316
317 # construct some packets and flows, send to switch
318 pkt1 = simple_tcp_packet()
319 flow_mod_msg1 = self.buildFlowModMsg(pkt1, port0, port1)
320
321 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
322 flow_mod_msg2 = self.buildFlowModMsg(pkt2, port0, port2)
323
324 logging.info("Inserting flow1")
Rich Lane5c3151c2013-01-03 17:15:41 -0800325 self.controller.message_send(flow_mod_msg1)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700326 logging.info("Inserting flow2")
Rich Lane5c3151c2013-01-03 17:15:41 -0800327 self.controller.message_send(flow_mod_msg2)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700328 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
329
330 num_pkt1s = random.randint(5,10)
331 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
332 num_pkt2s = random.randint(10,15)
333 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
334 for i in range(0,num_pkt1s):
335 sendPacket(self, pkt1, port0, port1, test_timeout)
336 for i in range(0,num_pkt2s):
337 sendPacket(self, pkt2, port0, port2, test_timeout)
338
339 # get individual port stats count
340 port_stats = {}
341 port_stats[ port0 ] = getStats(self, port0)
342 port_stats[ port1 ] = getStats(self, port1)
343 port_stats[ port2 ] = getStats(self, port2)
344
345 all_stats = getAllStats(self)
Rich Lanee53897c2012-10-30 09:40:13 -0700346 self.assertEqual(port_stats[ port0 ], all_stats[ port0 ])
347 self.assertEqual(port_stats[ port1 ], all_stats[ port1 ])
348 self.assertEqual(port_stats[ port2 ], all_stats[ port2 ])