blob: 29b04aed60e7ed0bea5d64cf4b5618c462eff266 [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 Lane32bf9482013-01-03 17:26:30 -0800151 delete_all_flows(self.controller)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700152
153 # build packet
154 pkt = simple_tcp_packet()
Ed Swierk99a74de2012-08-22 06:40:54 -0700155 match = packet_to_flow_match(self, pkt)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700156 match.wildcards &= ~ofp.OFPFW_IN_PORT
157 self.assertTrue(match is not None,
158 "Could not generate flow match from pkt")
159 act = action.action_output()
160
161 # build flow
162 ingress_port = of_ports[0];
163 egress_port = of_ports[1];
Rich Lane9a003812012-10-04 17:17:59 -0700164 logging.info("Ingress " + str(ingress_port) +
Shudong Zhoudf510a82012-08-03 18:08:40 -0700165 " to egress " + str(egress_port))
166 match.in_port = ingress_port
167 flow_mod_msg = message.flow_mod()
168 flow_mod_msg.match = match
169 flow_mod_msg.cookie = random.randint(0,9007199254740992)
170 flow_mod_msg.buffer_id = 0xffffffff
171 flow_mod_msg.idle_timeout = 0
172 flow_mod_msg.hard_timeout = 0
173 act.port = egress_port
Rich Lanee30455b2013-01-03 16:24:44 -0800174 flow_mod_msg.actions.add(act)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700175
176 # send flow
Rich Lane9a003812012-10-04 17:17:59 -0700177 logging.info("Inserting flow")
Rich Lane5c3151c2013-01-03 17:15:41 -0800178 self.controller.message_send(flow_mod_msg)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700179 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
180
Shudong Zhou50051c72012-08-06 16:53:46 -0700181 # get initial port stats count
182 initTxInPort, initRxInPort = getStats(self, ingress_port)
183 initTxOutPort, initRxOutPort = getStats(self, egress_port)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700184
185 # send packet N times
186 num_sends = random.randint(10,20)
Rich Lane9a003812012-10-04 17:17:59 -0700187 logging.info("Sending " + str(num_sends) + " test packets")
Shudong Zhoudf510a82012-08-03 18:08:40 -0700188 for i in range(0,num_sends):
189 sendPacket(self, pkt, ingress_port, egress_port,
190 test_timeout)
191
Shudong Zhou50051c72012-08-06 16:53:46 -0700192 verifyStats(self, ingress_port, test_timeout, initTxInPort, initRxInPort + num_sends)
193 verifyStats(self, egress_port, test_timeout, initTxOutPort + num_sends, initRxOutPort)
Shudong Zhou1d423922012-08-04 16:45:02 -0700194
Rich Laneb90a1c42012-10-05 09:16:05 -0700195class MultiFlowStats(base_tests.SimpleDataPlane):
Shudong Zhou1d423922012-08-04 16:45:02 -0700196 """
197 Verify flow stats are properly retrieved.
198
199 Generate two packets and install two matching flows
200 Send some number of packets
201 Send a port stats request to get packet count
202 Verify that the packet counter has incremented
203 """
204
205 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
Ed Swierk99a74de2012-08-22 06:40:54 -0700206 match = packet_to_flow_match(self, pkt)
Shudong Zhou1d423922012-08-04 16:45:02 -0700207 match.wildcards &= ~ofp.OFPFW_IN_PORT
208 self.assertTrue(match is not None,
209 "Could not generate flow match from pkt")
210 match.in_port = ingress_port
211
212 flow_mod_msg = message.flow_mod()
213 flow_mod_msg.match = match
214 flow_mod_msg.cookie = random.randint(0,9007199254740992)
215 flow_mod_msg.buffer_id = 0xffffffff
216 flow_mod_msg.idle_timeout = 0
217 flow_mod_msg.hard_timeout = 0
218 act = action.action_output()
219 act.port = egress_port
Rich Lanee30455b2013-01-03 16:24:44 -0800220 flow_mod_msg.actions.add(act)
Shudong Zhou1d423922012-08-04 16:45:02 -0700221
Rich Lane9a003812012-10-04 17:17:59 -0700222 logging.info("Ingress " + str(ingress_port) +
Shudong Zhou1d423922012-08-04 16:45:02 -0700223 " to egress " + str(egress_port))
224
225 return flow_mod_msg
226
Shudong Zhou1d423922012-08-04 16:45:02 -0700227 def runTest(self):
Shudong Zhou1d423922012-08-04 16:45:02 -0700228 # TODO: set from command-line parameter
229 test_timeout = 60
230
Rich Lane477f4812012-10-04 22:49:00 -0700231 of_ports = config["port_map"].keys()
Shudong Zhou1d423922012-08-04 16:45:02 -0700232 of_ports.sort()
233 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
234 ingress_port = of_ports[0];
235 egress_port1 = of_ports[1];
236 egress_port2 = of_ports[2];
237
Rich Lane32bf9482013-01-03 17:26:30 -0800238 delete_all_flows(self.controller)
Shudong Zhou1d423922012-08-04 16:45:02 -0700239
240 pkt1 = simple_tcp_packet()
241 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
242
243 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
244 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
245
Rich Lane9a003812012-10-04 17:17:59 -0700246 logging.info("Inserting flow1")
Rich Lane5c3151c2013-01-03 17:15:41 -0800247 self.controller.message_send(flow_mod_msg1)
Rich Lane9a003812012-10-04 17:17:59 -0700248 logging.info("Inserting flow2")
Rich Lane5c3151c2013-01-03 17:15:41 -0800249 self.controller.message_send(flow_mod_msg2)
Shudong Zhou1d423922012-08-04 16:45:02 -0700250 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
251
Shudong Zhou50051c72012-08-06 16:53:46 -0700252 # get initial port stats count
253 initTxInPort, initRxInPort = getStats(self, ingress_port)
254 initTxOutPort1, initRxOutPort1 = getStats(self, egress_port1)
255 initTxOutPort2, initRxOutPort2 = getStats(self, egress_port2)
256
Shudong Zhou1d423922012-08-04 16:45:02 -0700257 num_pkt1s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700258 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700259 num_pkt2s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700260 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700261 for i in range(0,num_pkt1s):
262 sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
263 for i in range(0,num_pkt2s):
264 sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
Shudong Zhou50051c72012-08-06 16:53:46 -0700265
266 verifyStats(self, ingress_port, test_timeout,
267 initTxInPort, initRxInPort + num_pkt1s + num_pkt2s)
268 verifyStats(self, egress_port1, test_timeout,
269 initTxOutPort1 + num_pkt1s, initRxOutPort1)
270 verifyStats(self, egress_port2, test_timeout,
271 initTxOutPort2 + num_pkt2s, initRxOutPort2)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700272
273class AllPortStats(base_tests.SimpleDataPlane):
274 """
275 Verify all port stats are properly retrieved.
276
277 First, get stats from each port. Then get all port stats, verify
278 consistency with single port stats.
279 """
280
281 # TODO: This is copied from MultiFlowStats. Need to combine.
282 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
283 match = packet_to_flow_match(self, pkt)
284 match.wildcards &= ~ofp.OFPFW_IN_PORT
285 self.assertTrue(match is not None,
286 "Could not generate flow match from pkt")
287 match.in_port = ingress_port
288
289 flow_mod_msg = message.flow_mod()
290 flow_mod_msg.match = match
291 flow_mod_msg.cookie = random.randint(0,9007199254740992)
292 flow_mod_msg.buffer_id = 0xffffffff
293 flow_mod_msg.idle_timeout = 0
294 flow_mod_msg.hard_timeout = 0
295 act = action.action_output()
296 act.port = egress_port
Rich Lanee30455b2013-01-03 16:24:44 -0800297 flow_mod_msg.actions.add(act)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700298
299 logging.info("Ingress " + str(ingress_port) +
300 " to egress " + str(egress_port))
301
302 return flow_mod_msg
303
304 def runTest(self):
305 # TODO: set from command-line parameter
306 test_timeout = 60
307
308 of_ports = config["port_map"].keys()
309 of_ports.sort()
310 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
311 port0 = of_ports[0];
312 port1 = of_ports[1];
313 port2 = of_ports[2];
314
315 # construct some packets and flows, send to switch
316 pkt1 = simple_tcp_packet()
317 flow_mod_msg1 = self.buildFlowModMsg(pkt1, port0, port1)
318
319 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
320 flow_mod_msg2 = self.buildFlowModMsg(pkt2, port0, port2)
321
322 logging.info("Inserting flow1")
Rich Lane5c3151c2013-01-03 17:15:41 -0800323 self.controller.message_send(flow_mod_msg1)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700324 logging.info("Inserting flow2")
Rich Lane5c3151c2013-01-03 17:15:41 -0800325 self.controller.message_send(flow_mod_msg2)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700326 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
327
328 num_pkt1s = random.randint(5,10)
329 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
330 num_pkt2s = random.randint(10,15)
331 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
332 for i in range(0,num_pkt1s):
333 sendPacket(self, pkt1, port0, port1, test_timeout)
334 for i in range(0,num_pkt2s):
335 sendPacket(self, pkt2, port0, port2, test_timeout)
336
337 # get individual port stats count
338 port_stats = {}
339 port_stats[ port0 ] = getStats(self, port0)
340 port_stats[ port1 ] = getStats(self, port1)
341 port_stats[ port2 ] = getStats(self, port2)
342
343 all_stats = getAllStats(self)
Rich Lanee53897c2012-10-30 09:40:13 -0700344 self.assertEqual(port_stats[ port0 ], all_stats[ port0 ])
345 self.assertEqual(port_stats[ port1 ], all_stats[ port1 ])
346 self.assertEqual(port_stats[ port2 ], all_stats[ port2 ])