blob: d2ade19dea54d65ce4613a60d20af3201ec00b8f [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.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 Lane32bf9482013-01-03 17:26:30 -0800152 delete_all_flows(self.controller)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700153
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)
Rich Lane3a261d52013-01-03 17:45:08 -0800180 do_barrier(self.controller)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700181
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 Lane32bf9482013-01-03 17:26:30 -0800239 delete_all_flows(self.controller)
Shudong Zhou1d423922012-08-04 16:45:02 -0700240
241 pkt1 = simple_tcp_packet()
242 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
243
244 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
245 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
246
Rich Lane9a003812012-10-04 17:17:59 -0700247 logging.info("Inserting flow1")
Rich Lane5c3151c2013-01-03 17:15:41 -0800248 self.controller.message_send(flow_mod_msg1)
Rich Lane9a003812012-10-04 17:17:59 -0700249 logging.info("Inserting flow2")
Rich Lane5c3151c2013-01-03 17:15:41 -0800250 self.controller.message_send(flow_mod_msg2)
Rich Lane3a261d52013-01-03 17:45:08 -0800251 do_barrier(self.controller)
Shudong Zhou1d423922012-08-04 16:45:02 -0700252
Shudong Zhou50051c72012-08-06 16:53:46 -0700253 # get initial port stats count
254 initTxInPort, initRxInPort = getStats(self, ingress_port)
255 initTxOutPort1, initRxOutPort1 = getStats(self, egress_port1)
256 initTxOutPort2, initRxOutPort2 = getStats(self, egress_port2)
257
Shudong Zhou1d423922012-08-04 16:45:02 -0700258 num_pkt1s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700259 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700260 num_pkt2s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700261 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700262 for i in range(0,num_pkt1s):
263 sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
264 for i in range(0,num_pkt2s):
265 sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
Shudong Zhou50051c72012-08-06 16:53:46 -0700266
267 verifyStats(self, ingress_port, test_timeout,
268 initTxInPort, initRxInPort + num_pkt1s + num_pkt2s)
269 verifyStats(self, egress_port1, test_timeout,
270 initTxOutPort1 + num_pkt1s, initRxOutPort1)
271 verifyStats(self, egress_port2, test_timeout,
272 initTxOutPort2 + num_pkt2s, initRxOutPort2)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700273
274class AllPortStats(base_tests.SimpleDataPlane):
275 """
276 Verify all port stats are properly retrieved.
277
278 First, get stats from each port. Then get all port stats, verify
279 consistency with single port stats.
280 """
281
282 # TODO: This is copied from MultiFlowStats. Need to combine.
283 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
284 match = packet_to_flow_match(self, pkt)
285 match.wildcards &= ~ofp.OFPFW_IN_PORT
286 self.assertTrue(match is not None,
287 "Could not generate flow match from pkt")
288 match.in_port = ingress_port
289
290 flow_mod_msg = message.flow_mod()
291 flow_mod_msg.match = match
292 flow_mod_msg.cookie = random.randint(0,9007199254740992)
293 flow_mod_msg.buffer_id = 0xffffffff
294 flow_mod_msg.idle_timeout = 0
295 flow_mod_msg.hard_timeout = 0
296 act = action.action_output()
297 act.port = egress_port
Rich Lanee30455b2013-01-03 16:24:44 -0800298 flow_mod_msg.actions.add(act)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700299
300 logging.info("Ingress " + str(ingress_port) +
301 " to egress " + str(egress_port))
302
303 return flow_mod_msg
304
305 def runTest(self):
306 # TODO: set from command-line parameter
307 test_timeout = 60
308
309 of_ports = config["port_map"].keys()
310 of_ports.sort()
311 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
312 port0 = of_ports[0];
313 port1 = of_ports[1];
314 port2 = of_ports[2];
315
316 # construct some packets and flows, send to switch
317 pkt1 = simple_tcp_packet()
318 flow_mod_msg1 = self.buildFlowModMsg(pkt1, port0, port1)
319
320 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
321 flow_mod_msg2 = self.buildFlowModMsg(pkt2, port0, port2)
322
323 logging.info("Inserting flow1")
Rich Lane5c3151c2013-01-03 17:15:41 -0800324 self.controller.message_send(flow_mod_msg1)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700325 logging.info("Inserting flow2")
Rich Lane5c3151c2013-01-03 17:15:41 -0800326 self.controller.message_send(flow_mod_msg2)
Rich Lane3a261d52013-01-03 17:45:08 -0800327 do_barrier(self.controller)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700328
329 num_pkt1s = random.randint(5,10)
330 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
331 num_pkt2s = random.randint(10,15)
332 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
333 for i in range(0,num_pkt1s):
334 sendPacket(self, pkt1, port0, port1, test_timeout)
335 for i in range(0,num_pkt2s):
336 sendPacket(self, pkt2, port0, port2, test_timeout)
337
338 # get individual port stats count
339 port_stats = {}
340 port_stats[ port0 ] = getStats(self, port0)
341 port_stats[ port1 ] = getStats(self, port1)
342 port_stats[ port2 ] = getStats(self, port2)
343
344 all_stats = getAllStats(self)
Rich Lanee53897c2012-10-30 09:40:13 -0700345 self.assertEqual(port_stats[ port0 ], all_stats[ port0 ])
346 self.assertEqual(port_stats[ port1 ], all_stats[ port1 ])
347 self.assertEqual(port_stats[ port2 ], all_stats[ port2 ])