blob: 4b02a291f2322901d65140ab1c63d184463a2f24 [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
175 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
176
177 # send flow
Rich Lane9a003812012-10-04 17:17:59 -0700178 logging.info("Inserting flow")
Shudong Zhoudf510a82012-08-03 18:08:40 -0700179 rv = self.controller.message_send(flow_mod_msg)
180 self.assertTrue(rv != -1, "Error installing flow mod")
181 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
182
Shudong Zhou50051c72012-08-06 16:53:46 -0700183 # get initial port stats count
184 initTxInPort, initRxInPort = getStats(self, ingress_port)
185 initTxOutPort, initRxOutPort = getStats(self, egress_port)
Shudong Zhoudf510a82012-08-03 18:08:40 -0700186
187 # send packet N times
188 num_sends = random.randint(10,20)
Rich Lane9a003812012-10-04 17:17:59 -0700189 logging.info("Sending " + str(num_sends) + " test packets")
Shudong Zhoudf510a82012-08-03 18:08:40 -0700190 for i in range(0,num_sends):
191 sendPacket(self, pkt, ingress_port, egress_port,
192 test_timeout)
193
Shudong Zhou50051c72012-08-06 16:53:46 -0700194 verifyStats(self, ingress_port, test_timeout, initTxInPort, initRxInPort + num_sends)
195 verifyStats(self, egress_port, test_timeout, initTxOutPort + num_sends, initRxOutPort)
Shudong Zhou1d423922012-08-04 16:45:02 -0700196
Rich Laneb90a1c42012-10-05 09:16:05 -0700197class MultiFlowStats(base_tests.SimpleDataPlane):
Shudong Zhou1d423922012-08-04 16:45:02 -0700198 """
199 Verify flow stats are properly retrieved.
200
201 Generate two packets and install two matching flows
202 Send some number of packets
203 Send a port stats request to get packet count
204 Verify that the packet counter has incremented
205 """
206
207 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
Ed Swierk99a74de2012-08-22 06:40:54 -0700208 match = packet_to_flow_match(self, pkt)
Shudong Zhou1d423922012-08-04 16:45:02 -0700209 match.wildcards &= ~ofp.OFPFW_IN_PORT
210 self.assertTrue(match is not None,
211 "Could not generate flow match from pkt")
212 match.in_port = ingress_port
213
214 flow_mod_msg = message.flow_mod()
215 flow_mod_msg.match = match
216 flow_mod_msg.cookie = random.randint(0,9007199254740992)
217 flow_mod_msg.buffer_id = 0xffffffff
218 flow_mod_msg.idle_timeout = 0
219 flow_mod_msg.hard_timeout = 0
220 act = action.action_output()
221 act.port = egress_port
222 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
223
Rich Lane9a003812012-10-04 17:17:59 -0700224 logging.info("Ingress " + str(ingress_port) +
Shudong Zhou1d423922012-08-04 16:45:02 -0700225 " to egress " + str(egress_port))
226
227 return flow_mod_msg
228
Shudong Zhou1d423922012-08-04 16:45:02 -0700229 def runTest(self):
Shudong Zhou1d423922012-08-04 16:45:02 -0700230 # TODO: set from command-line parameter
231 test_timeout = 60
232
Rich Lane477f4812012-10-04 22:49:00 -0700233 of_ports = config["port_map"].keys()
Shudong Zhou1d423922012-08-04 16:45:02 -0700234 of_ports.sort()
235 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
236 ingress_port = of_ports[0];
237 egress_port1 = of_ports[1];
238 egress_port2 = of_ports[2];
239
Rich Lane9a003812012-10-04 17:17:59 -0700240 rc = delete_all_flows(self.controller)
Shudong Zhou1d423922012-08-04 16:45:02 -0700241 self.assertEqual(rc, 0, "Failed to delete all flows")
242
243 pkt1 = simple_tcp_packet()
244 flow_mod_msg1 = self.buildFlowModMsg(pkt1, ingress_port, egress_port1)
245
246 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
247 flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
248
Rich Lane9a003812012-10-04 17:17:59 -0700249 logging.info("Inserting flow1")
Shudong Zhou1d423922012-08-04 16:45:02 -0700250 rv = self.controller.message_send(flow_mod_msg1)
251 self.assertTrue(rv != -1, "Error installing flow mod")
Rich Lane9a003812012-10-04 17:17:59 -0700252 logging.info("Inserting flow2")
Shudong Zhou1d423922012-08-04 16:45:02 -0700253 rv = self.controller.message_send(flow_mod_msg2)
254 self.assertTrue(rv != -1, "Error installing flow mod")
255 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
256
Shudong Zhou50051c72012-08-06 16:53:46 -0700257 # get initial port stats count
258 initTxInPort, initRxInPort = getStats(self, ingress_port)
259 initTxOutPort1, initRxOutPort1 = getStats(self, egress_port1)
260 initTxOutPort2, initRxOutPort2 = getStats(self, egress_port2)
261
Shudong Zhou1d423922012-08-04 16:45:02 -0700262 num_pkt1s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700263 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700264 num_pkt2s = random.randint(10,30)
Rich Lane9a003812012-10-04 17:17:59 -0700265 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
Shudong Zhou1d423922012-08-04 16:45:02 -0700266 for i in range(0,num_pkt1s):
267 sendPacket(self, pkt1, ingress_port, egress_port1, test_timeout)
268 for i in range(0,num_pkt2s):
269 sendPacket(self, pkt2, ingress_port, egress_port2, test_timeout)
Shudong Zhou50051c72012-08-06 16:53:46 -0700270
271 verifyStats(self, ingress_port, test_timeout,
272 initTxInPort, initRxInPort + num_pkt1s + num_pkt2s)
273 verifyStats(self, egress_port1, test_timeout,
274 initTxOutPort1 + num_pkt1s, initRxOutPort1)
275 verifyStats(self, egress_port2, test_timeout,
276 initTxOutPort2 + num_pkt2s, initRxOutPort2)
Shudong Zhou1a5b0822012-10-29 22:03:34 -0700277
278class AllPortStats(base_tests.SimpleDataPlane):
279 """
280 Verify all port stats are properly retrieved.
281
282 First, get stats from each port. Then get all port stats, verify
283 consistency with single port stats.
284 """
285
286 # TODO: This is copied from MultiFlowStats. Need to combine.
287 def buildFlowModMsg(self, pkt, ingress_port, egress_port):
288 match = packet_to_flow_match(self, pkt)
289 match.wildcards &= ~ofp.OFPFW_IN_PORT
290 self.assertTrue(match is not None,
291 "Could not generate flow match from pkt")
292 match.in_port = ingress_port
293
294 flow_mod_msg = message.flow_mod()
295 flow_mod_msg.match = match
296 flow_mod_msg.cookie = random.randint(0,9007199254740992)
297 flow_mod_msg.buffer_id = 0xffffffff
298 flow_mod_msg.idle_timeout = 0
299 flow_mod_msg.hard_timeout = 0
300 act = action.action_output()
301 act.port = egress_port
302 self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
303
304 logging.info("Ingress " + str(ingress_port) +
305 " to egress " + str(egress_port))
306
307 return flow_mod_msg
308
309 def runTest(self):
310 # TODO: set from command-line parameter
311 test_timeout = 60
312
313 of_ports = config["port_map"].keys()
314 of_ports.sort()
315 self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
316 port0 = of_ports[0];
317 port1 = of_ports[1];
318 port2 = of_ports[2];
319
320 # construct some packets and flows, send to switch
321 pkt1 = simple_tcp_packet()
322 flow_mod_msg1 = self.buildFlowModMsg(pkt1, port0, port1)
323
324 pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
325 flow_mod_msg2 = self.buildFlowModMsg(pkt2, port0, port2)
326
327 logging.info("Inserting flow1")
328 rv = self.controller.message_send(flow_mod_msg1)
329 self.assertTrue(rv != -1, "Error installing flow mod")
330 logging.info("Inserting flow2")
331 rv = self.controller.message_send(flow_mod_msg2)
332 self.assertTrue(rv != -1, "Error installing flow mod")
333 self.assertEqual(do_barrier(self.controller), 0, "Barrier failed")
334
335 num_pkt1s = random.randint(5,10)
336 logging.info("Sending " + str(num_pkt1s) + " pkt1s")
337 num_pkt2s = random.randint(10,15)
338 logging.info("Sending " + str(num_pkt2s) + " pkt2s")
339 for i in range(0,num_pkt1s):
340 sendPacket(self, pkt1, port0, port1, test_timeout)
341 for i in range(0,num_pkt2s):
342 sendPacket(self, pkt2, port0, port2, test_timeout)
343
344 # get individual port stats count
345 port_stats = {}
346 port_stats[ port0 ] = getStats(self, port0)
347 port_stats[ port1 ] = getStats(self, port1)
348 port_stats[ port2 ] = getStats(self, port2)
349
350 all_stats = getAllStats(self)
351 assert(port_stats[ port0 ] == all_stats[ port0 ])
352 assert(port_stats[ port1 ] == all_stats[ port1 ])
353 assert(port_stats[ port2 ] == all_stats[ port2 ])