blob: 5b8cb3ba5ed1494a8e5c4f937e1c007bee9dfc04 [file] [log] [blame]
Howard Pershc7963582012-03-29 10:02:59 -07001"""
2Flow query test case.
3
4Attempts to fill switch to capacity with randomized flows, and ensure that they all are read back correctly.
5"""
6
7import logging
8
9import unittest
10import random
11
12import oftest.controller as controller
13import oftest.cstruct as ofp
14import oftest.message as message
15import oftest.dataplane as dataplane
16import oftest.action as action
17import oftest.action_list as action_list
18import oftest.parse as parse
19import pktact
20import basic
21
22from testutils import *
23from time import sleep
24
25#@var port_map Local copy of the configuration map from OF port
26# numbers to OS interfaces
27pa_port_map = None
28#@var pa_logger Local logger object
29pa_logger = None
30#@var pa_config Local copy of global configuration data
31pa_config = None
32
33def test_set_init(config):
34 """
35 Set up function for packet action test classes
36
37 @param config The configuration dictionary; see oft
38 """
39
40 basic.test_set_init(config)
41
42 global pa_port_map
43 global pa_logger
44 global pa_config
45
46 pa_logger = logging.getLogger("pkt_act")
47 pa_logger.info("Initializing test set")
48 pa_port_map = config["port_map"]
49 pa_config = config
50
51
52def shuffle(list):
53 n = len(list)
54 lim = n * n
55 i = 0
56 while i < lim:
57 a = random.randint(0, n - 1)
58 b = random.randint(0, n - 1)
59 temp = list[a]
60 list[a] = list[b]
61 list[b] = temp
62 i = i + 1
63 return list
64
65
66def rand_dl_addr():
67 return [random.randint(0, 255) & ~1, random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)]
68
69
70def rand_nw_addr():
71 return random.randint(0, (1 << 32) - 1)
72
73
74# TBD - These don't belong here
75
76all_wildcards = [ofp.OFPFW_IN_PORT, \
77 ofp.OFPFW_DL_VLAN, \
78 ofp.OFPFW_DL_SRC, \
79 ofp.OFPFW_DL_DST, \
80 ofp.OFPFW_DL_TYPE, \
81 ofp.OFPFW_NW_PROTO, \
82 ofp.OFPFW_TP_SRC, \
83 ofp.OFPFW_TP_DST, \
84 ofp.OFPFW_NW_SRC_ALL, \
85 ofp.OFPFW_NW_DST_ALL, \
86 ofp.OFPFW_DL_VLAN_PCP, \
87 ofp.OFPFW_NW_TOS \
88 ]
89
90all_actions = [ofp.OFPAT_OUTPUT,
91 ofp.OFPAT_SET_VLAN_VID,
92 ofp.OFPAT_SET_VLAN_PCP,
93 ofp.OFPAT_STRIP_VLAN,
94 ofp.OFPAT_SET_DL_SRC,
95 ofp.OFPAT_SET_DL_DST,
96 ofp.OFPAT_SET_NW_SRC,
97 ofp.OFPAT_SET_NW_DST,
98 ofp.OFPAT_SET_NW_TOS,
99 ofp.OFPAT_SET_TP_SRC,
100 ofp.OFPAT_SET_TP_DST,
101 ofp.OFPAT_ENQUEUE
102 ]
103
104
105class flow_cfg:
106 # Members:
107 # - match
108 # - idle_timeout
109 # - hard_timeout
110 # - priority
111 # - action_list
112
113 def __init__(self):
114 self.match = parse.ofp_match()
115 self.match.wildcards = ofp.OFPFW_ALL
116 self.idle_timeout = 0
117 self.hard_timeout = 0
118 self.priority = 0
119 self.actions = action_list.action_list()
120
121 def __eq__(self, x):
122 if self.match != x.match:
123 return False
124 if self.idle_timeout != x.idle_timeout:
125 return False
126 if self.hard_timeout != x.hard_timeout:
127 return False
128 if self.priority != x.priority:
129 return False
130 return self.actions == x.actions # N.B. Action lists are ordered
131
132 def rand(self, valid_wildcards, valid_actions, valid_ports):
133 # TBD - Are IP addr wildcard specs all or nothing, valid wildcard reported as all 1s or all 0s?
134 self.match.wildcards = valid_wildcards
135 if (self.match.wildcards & ofp.OFPFW_NW_SRC_MASK) == ofp.OFPFW_NW_SRC_MASK:
136 self.match.wildcards = (self.match.wildcards & ~ofp.OFPFW_NW_SRC_MASK) | ofp.OFPFW_NW_SRC_ALL
137 if (self.match.wildcards & ofp.OFPFW_NW_DST_MASK) == ofp.OFPFW_NW_DST_MASK:
138 self.match.wildcards = (self.match.wildcards & ~ofp.OFPFW_NW_DST_MASK) | ofp.OFPFW_NW_DST_ALL
139
140 exact = True if random.randint(1, 100) == 1 else False
141
142 for w in all_wildcards:
143 if not exact and (w & valid_wildcards) != 0:
144 if random.randint(1, 100) <= 50:
145 continue
146
147 if w == ofp.OFPFW_IN_PORT:
148 self.match.in_port = valid_ports[random.randint(0, len(valid_ports) - 1)].port_no
149 self.match.wildcards = self.match.wildcards & ~w
150 elif w == ofp.OFPFW_DL_VLAN:
151 self.match.vl_vlan = random.randint(1, 4094)
152 self.match.wildcards = self.match.wildcards & ~w
153 elif w == ofp.OFPFW_DL_SRC:
154 self.match.dl_src = rand_dl_addr()
155 self.match.wildcards = self.match.wildcards & ~w
156 elif w == ofp.OFPFW_DL_DST:
157 self.match.dl_dst = rand_dl_addr()
158 self.match.wildcards = self.match.wildcards & ~w
159 elif w == ofp.OFPFW_DL_TYPE:
160 if (self.match.wildcards & w) != 0:
161 self.match.dl_type = random.randint(0, (1 << 16) - 1)
162 self.match.wildcards = self.match.wildcards & ~w
163 elif w == ofp.OFPFW_NW_PROTO:
164 if (self.match.wildcards & w) != 0:
165 self.match.nw_proto = random.randint(0, (1 << 8) - 1)
166 self.match.wildcards = self.match.wildcards & ~w
167 self.match.dl_type = 0x0800
168 self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
169 elif w == ofp.OFPFW_TP_SRC:
170 self.match.tp_src = random.randint(0, (1 << 16) - 1)
171 self.match.wildcards = self.match.wildcards & ~w
172 self.match.nw_proto = [1, 6, 17][random.randint(0, 2)]
173 self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_NW_PROTO
174 self.match.dl_type = 0x0800
175 self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
176 elif w == ofp.OFPFW_TP_DST:
177 self.match.tp_dst = random.randint(0, (1 << 16) - 1)
178 self.match.wildcards = self.match.wildcards & ~w
179 self.match.nw_proto = [1, 6, 17][random.randint(0, 2)]
180 self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_NW_PROTO
181 self.match.dl_type = 0x0800
182 self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
183 elif w == ofp.OFPFW_NW_SRC_MASK:
184 n = 0 if exact else random.randint(0, 31)
185 self.match.nw_src = rand_nw_addr() & ~((1 << n) - 1)
186 self.match.wildcards = (self.match.wildcards & ~w) | (n << ofp.OFPFW_NW_SRC_SHIFT)
187 self.match.dl_type = [0x0800, 0x0806][random.randint(0, 1)]
188 self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
189 elif w == ofp.OFPFW_NW_DST_MASK:
190 n = 0 if exact else random.randint(0, 31)
191 self.match.nw_dst = rand_nw_addr() & ~((1 << n) - 1)
192 self.match.wildcards = (self.match.wildcards & ~w) | (n << ofp.OFPFW_NW_DST_SHIFT)
193 self.match.dl_type = [0x0800, 0x0806][random.randint(0, 1)]
194 self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
195 elif w == ofp.OFPFW_DL_VLAN_PCP:
196 self.match.dl_vlan_pcp = random.randint(0, (1 << 3) - 1)
197 self.match.wildcards = self.match.wildcards & ~w
198 elif w == ofp.OFPFW_NW_TOS:
199 while True:
200 self.match.nw_tos = random.randint(0, (1 << 8) - 1)
201 if (self.match.nw_tos & 3) == 0:
202 break
203 self.match.wildcards = self.match.wildcards & ~w
204 self.match.dl_type = 0x0800
205 self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE
206
207 # N.B. Don't make the timeout too short, else the flow might disappear before we
208 # get a chance to check for it.
209 t = random.randint(0, 65535)
210 self.idle_timeout = 0 if t < 60 else t
211 t = random.randint(0, 65535)
212 self.hard_timeout = 0 if t < 60 else t
213 self.priority = 65535 if exact else random.randint(1, 65535)
214
215 # N.B. Action lists are ordered
216 supported_action_idxs = []
217 ai = 0
218 while ai < len(all_actions):
219 if ((1 << all_actions[ai]) & valid_actions) != 0:
220 supported_action_idxs.append(ai)
221 ai = ai + 1
222
223 supported_action_idxs = shuffle(supported_action_idxs)
224 supported_action_idxs = supported_action_idxs[0 : random.randint(1, len(supported_action_idxs) - 1)]
225
226 self.actions = action_list.action_list()
227 for ai in supported_action_idxs:
228 a = all_actions[ai]
229
230 if a == ofp.OFPAT_OUTPUT:
231 # TBD - Output actions are clustered in list, spread them out?
232 port_idxs = shuffle(range(len(valid_ports)))
233 port_idxs = port_idxs[0 : random.randint(1, len(valid_ports) - 1)]
234 for pi in port_idxs:
235 act = action.action_output()
236 act.port = valid_ports[pi].port_no
237 self.actions.add(act)
238 elif a == ofp.OFPAT_SET_VLAN_VID:
239 act = action.action_set_vlan_vid()
240 act.vlan_vid = random.randint(1, 4094)
241 self.actions.add(act)
242 elif a == ofp.OFPAT_SET_VLAN_PCP:
243 # TBD - Temporaily removed, broken in Indigo
244 #act = action.action_set_vlan_pcp()
245 #act.vlan_pcp = random.randint(0, (1 << 3) - 1)
246 pass
247 elif a == ofp.OFPAT_STRIP_VLAN:
248 act = action.action_strip_vlan()
249 self.actions.add(act)
250 elif a == ofp.OFPAT_SET_DL_SRC:
251 act = action.action_set_dl_src()
252 act.dl_addr = rand_dl_addr()
253 self.actions.add(act)
254 elif a == ofp.OFPAT_SET_DL_DST:
255 act = action.action_set_dl_dst()
256 act.dl_addr = rand_dl_addr()
257 self.actions.add(act)
258 elif a == ofp.OFPAT_SET_NW_SRC:
259 act = action.action_set_nw_src()
260 act.nw_addr = rand_nw_addr()
261 self.actions.add(act)
262 elif a == ofp.OFPAT_SET_NW_DST:
263 act = action.action_set_nw_dst()
264 act.nw_addr = rand_nw_addr()
265 self.actions.add(act)
266 elif a == ofp.OFPAT_SET_NW_TOS:
267 act = action.action_set_nw_tos()
268 act.nw_tos = random.randint(0, (1 << 8) - 1)
269 self.actions.add(act)
270 elif a == ofp.OFPAT_SET_TP_SRC:
271 act = action.action_set_tp_src()
272 act.tp_port = random.randint(0, (1 << 16) - 1)
273 self.actions.add(act)
274 elif a == ofp.OFPAT_SET_TP_DST:
275 act = action.action_set_tp_dst()
276 act.tp_port = random.randint(0, (1 << 16) - 1)
277 self.actions.add(act)
278 elif a == ofp.OFPAT_ENQUEUE:
279 # TBD - Enqueue actions are clustered in list, spread them out?
280 port_idxs = shuffle(range(len(valid_ports)))
281 port_idxs = port_idxs[0 : random.randint(1, len(valid_ports) - 1)]
282 for pi in port_idxs:
283 act = action.action_enqueue()
284 act.port = valid_ports[pi].port_no
285 # TBD - Limits for queue number?
286 act.queue_id = random.randint(0, 7)
287 self.actions.add(act)
288
289 return self
290
291 def overlap(self, x):
292 if self.priority != x.priority:
293 return False
294 if (self.match.wildcards & ofp.OFPFW_IN_PORT) == 0 and (x.match.wildcards & ofp.OFPFW_IN_PORT) == 0 and self.match.in_port != x.match.in_port:
295 return False
296 if (self.match.wildcards & ofp.OFPFW_DL_VLAN) == 0 and (x.match.wildcards & ofp.OFPFW_DL_VLAN) == 0 and self.match.dl_vlan != x.match.dl_vlan:
297 return False
298 if (self.match.wildcards & ofp.OFPFW_DL_SRC) == 0 and (x.match.wildcards & ofp.OFPFW_DL_SRC) == 0 and self.match.dl_src != x.match.dl_src:
299 return False
300 if (self.match.wildcards & ofp.OFPFW_DL_DST) == 0 and (x.match.wildcards & ofp.OFPFW_DL_DST) == 0 and self.match.dl_dst != x.match.dl_dst:
301 return False
302 if (self.match.wildcards & ofp.OFPFW_DL_TYPE) == 0 and (x.match.wildcards & ofp.OFPFW_DL_TYPE) == 0 and self.match.dl_type != x.match.dl_type:
303 return False
304 if (self.match.wildcards & ofp.OFPFW_NW_PROTO) == 0 and (x.match.wildcards & ofp.OFPFW_NW_PROTO) == 0 and self.match.nw_proto != x.match.nw_proto:
305 return False
306 if (self.match.wildcards & ofp.OFPFW_TP_SRC) == 0 and (x.match.wildcards & ofp.OFPFW_TP_SRC) == 0 and self.match.tp_src != x.match.tp_src:
307 return False
308 if (self.match.wildcards & ofp.OFPFW_TP_DST) == 0 and (x.match.wildcards & ofp.OFPFW_TP_DST) == 0 and self.match.tp_dst != x.match.tp_dst:
309 return False
310 na = (self.match.wildcards & ofp.OFPFW_NW_SRC_MASK) >> ofp.OFPFW_NW_SRC_SHIFT
311 nb = (x.match.wildcards & ofp.OFPFW_NW_SRC_MASK) >> ofp.OFPFW_NW_SRC_SHIFT
312 if (na < 32 and nb < 32):
313 m = ~((1 << na) - 1) & ~((1 << nb) - 1)
314 if (self.match.nw_src & m) != (x.match.nw_src & m):
315 return False
316 na = (self.match.wildcards & ofp.OFPFW_NW_DST_MASK) >> ofp.OFPFW_NW_DST_SHIFT
317 nb = (x.match.wildcards & ofp.OFPFW_NW_DST_MASK) >> ofp.OFPFW_NW_DST_SHIFT
318 if (na < 32 and nb < 32):
319 m = ~((1 << na) - 1) & ~((1 << nb) - 1)
320 if (self.match.nw_dst & m) != (x.match.nw_dst & m):
321 return False
322 if (self.match.wildcards & ofp.OFPFW_DL_VLAN_PCP) == 0 and (x.match.wildcards & ofp.OFPFW_DL_VLAN_PCP) == 0 and self.match.dl_vlan_pcp != x.match.dl_vlan_pcp:
323 return False
324 if (self.match.wildcards & ofp.OFPFW_NW_TOS) == 0 and (x.match.wildcards & ofp.OFPFW_NW_TOS) == 0 and self.match.nw_tos != x.match.nw_tos:
325 return False
326 return True
327
328 def to_flow_mod_msg(self, msg):
329 msg.match = self.match
330 msg.idle_timeout = self.idle_timeout
331 msg.hard_timeout = self.hard_timeout
332 msg.priority = self.priority
333 msg.actions = self.actions
334 return msg
335
336 def from_flow_stat(self, msg):
337 self.match = msg.match
338 self.idle_timeout = msg.idle_timeout
339 self.hard_timeout = msg.hard_timeout
340 self.priority = msg.priority
341 self.actions = msg.actions
342
343
344class FlowQuery(basic.SimpleProtocol):
345 """
346 """
347
348 def test1(self, overlapf):
349 """
350 """
351
352 # Clear all flows from switch
353 self.logger.debug("Deleting all flows from switch")
354 rc = delete_all_flows(self.controller, pa_logger)
355 self.assertEqual(rc, 0, "Failed to delete all flows")
356
357 # Get valid port numbers
358 # Get number of tables supported
359 # Get actions supported by switch
360
361 self.logger.debug("Retrieving features from switch")
362 request = message.features_request()
363 (sw_features, pkt) = self.controller.transact(request, timeout=2)
364 self.assertTrue(sw_features is not None, "No reply to features_request")
365 self.logger.debug("Switch features -")
366 self.logger.debug("Number of tables: " + str(sw_features.n_tables))
367 self.logger.debug("Supported actions: " + hex(sw_features.actions))
368 self.logger.debug("Ports: " + str(map(lambda x: x.port_no, sw_features.ports)))
369
370 # For each table, get wildcards supported maximum number of flows
371
372 self.logger.debug("Retrieving table stats from switch")
373 request = message.table_stats_request()
374 (tbl_stats, pkt) = self.controller.transact(request, timeout=2)
375 self.assertTrue(tbl_stats is not None, "No reply to table_stats_request")
376 active_count = 0
377 tbl_idx = 0
378 while tbl_idx < sw_features.n_tables:
379 self.logger.debug("Table " + str(tbl_idx) + " - ")
380 self.logger.debug("Supported wildcards: " + hex(tbl_stats.stats[tbl_idx].wildcards))
381 self.logger.debug("Max entries: " + str(tbl_stats.stats[tbl_idx].max_entries))
382 self.logger.debug("Active count: " + str(tbl_stats.stats[tbl_idx].active_count))
383 active_count = active_count + tbl_stats.stats[tbl_idx].active_count
384 tbl_idx = tbl_idx + 1
385
386 self.assertEqual(active_count, 0, "Total number of active entries not 0 -- delete all flow failed?")
387
388
389 # For each table, think up flows to fill it
390
391 self.logger.debug("Creating flows")
392 num_flows = 0
393 num_overlaps = 0
394 tbl_flows = []
395 tbl_idx = 0
396 while tbl_idx < sw_features.n_tables:
397 flow_cfgs = []
398 flow_idx = 0
399 while flow_idx < tbl_stats.stats[tbl_idx].max_entries:
400 flow_out = flow_cfg().rand(tbl_stats.stats[tbl_idx].wildcards, sw_features.actions, sw_features.ports)
401 j = 0
402 while j < len(flow_cfgs):
403 if flow_out.overlap(flow_cfgs[j]):
404 break
405 j = j + 1
406 if j < len(flow_cfgs):
407 num_overlaps = num_overlaps + 1
408 flow_out.overlap = True
409 else:
410 flow_out.overlap = False
411 flow_cfgs.append(flow_out)
412 num_flows = num_flows + 1
413 flow_idx = flow_idx + 1
414 tbl_flows.append(flow_cfgs)
415 tbl_idx = tbl_idx + 1
416
417 self.logger.debug("Created " + str(num_flows) + " flows, with " + str(num_overlaps) + " overlaps")
418
419 # Send all flows to switch
420
421 self.logger.debug("Sending flows to switch")
422 flow_num = 1
423 tbl_idx = 0
424 while tbl_idx < sw_features.n_tables:
425 flow_idx = 0
426 while flow_idx < len(tbl_flows[tbl_idx]):
427 self.logger.debug("Sending flow number " + str(flow_num))
428 flow_mod_msg = message.flow_mod()
429 flow_mod_msg.buffer_id = 0xffffffff
430 flow_mod_msg.cookie = random.randint(0, (1 << 53) - 1)
431 tbl_flows[tbl_idx][flow_idx].to_flow_mod_msg(flow_mod_msg)
432 if overlapf:
433 flow_mod_msg.flags = flow_mod_msg.flags | ofp.OFPFF_CHECK_OVERLAP
434 rv = self.controller.message_send(flow_mod_msg)
435 self.assertTrue(rv != -1, "Error installing flow mod")
436 (errmsg, pkt) = self.controller.poll(ofp.OFPT_ERROR, 1) # TBD - Tune timeout for error message
437 if errmsg is not None:
438 # Got ERROR message
439 if errmsg.type == ofp.OFPET_FLOW_MOD_FAILED and errmsg.code == ofp.OFPFMFC_OVERLAP:
440 # Got "overlap" ERROR message
441 self.assertTrue(overlapf and tbl_flows[tbl_idx][flow_idx].overlap, "Got unexpected OVERLAP error message")
442 else:
443 self.assertTrue(False, "Got unexpected error message, type = " + str(errmsg.type) + ", code = " + str(errmsg.code))
444 else:
445 # Did not get ERROR message
446 self.assertTrue(not (overlapf and tbl_flows[tbl_idx][flow_idx].overlap), "Did not get expected OVERLAP message")
447
448 flow_idx = flow_idx + 1
449 flow_num = flow_num + 1
450 tbl_idx = tbl_idx + 1
451
452 # Send barrier, to make sure all flows are in
453
454 barrier = message.barrier_request()
455 (resp, pkt) = self.controller.transact(barrier, 5)
456 self.assertTrue(resp is not None, "Did not receive response to barrier request")
457
458 # Check number of flows reported in table stats
459
460 self.logger.debug("Verifying that table stats reports the correct number of active flows")
461 request = message.table_stats_request()
462 (tbl_stats_after, pkt) = self.controller.transact(request, timeout=2)
463 self.assertTrue(tbl_stats_after is not None, "No reply to table_stats_request")
464
465 num_flows_reported = 0
466 for ts in tbl_stats_after.stats:
467 num_flows_reported = num_flows_reported + ts.active_count
468
469 num_flows_expected = num_flows
470 if overlapf:
471 num_flows_expected = num_flows_expected - num_overlaps
472
473 self.assertEqual(num_flows_reported, num_flows_expected, "Incorrect number of flows returned by table stats, reported = " + str(num_flows_reported) + ", expected = " + str(num_flows_expected))
474
475 # Retrieve all flows from switch
476
477 self.logger.debug("Retrieving all flows from switch")
478 stat_req = message.flow_stats_request()
479 query_match = ofp.ofp_match()
480 query_match.wildcards = ofp.OFPFW_ALL
481 stat_req.match = query_match
482 stat_req.table_id = 0xff
483 stat_req.out_port = ofp.OFPP_NONE;
484 flow_stats, pkt = self.controller.transact(stat_req, timeout=2)
485 self.assertTrue(flow_stats is not None, "Get all flow stats failed")
486
487 # Verify retrieved flows
488
489 self.logger.debug("Verifying retrieved flows")
490
491 self.assertEqual(flow_stats.type, ofp.OFPST_FLOW, "Unexpected type of response message")
492
493 num_flows_reported = len(flow_stats.stats)
494
495 self.assertEqual(num_flows_reported, num_flows_expected, "Incorrect number of flows returned by table stats, reported = " + str(num_flows_reported) + ", expected = " + str(num_flows_expected))
496
497 tbl_idx = 0
498 while tbl_idx < sw_features.n_tables:
499 flow_idx = 0
500 while flow_idx < len(tbl_flows[tbl_idx]):
501 tbl_flows[tbl_idx][flow_idx].resp_matched = False
502 flow_idx = flow_idx + 1
503 tbl_idx = tbl_idx + 1
504
505 num_resp_flows_matched = 0
506 for flow_stat in flow_stats.stats:
507 flow_in = flow_cfg()
508 flow_in.from_flow_stat(flow_stat)
509
510 tbl_idx = 0
511 while tbl_idx < sw_features.n_tables:
512 flow_idx = 0
513 while flow_idx < len(tbl_flows[tbl_idx]):
514 f = tbl_flows[tbl_idx][flow_idx]
515 if not f.resp_matched and (not overlapf or not f.overlap) and f == flow_in:
516 f.resp_matched = True
517 num_resp_flows_matched = num_resp_flows_matched + 1
518 break
519 flow_idx = flow_idx + 1
520 self.assertTrue(flow_idx < len(tbl_flows[tbl_idx]), "Reponse flow does not match any configured flow")
521 tbl_idx = tbl_idx + 1
522
523 self.assertEqual(num_resp_flows_matched, num_flows_expected, "Configured flow(s) missing in response, num_resp_flows_matched = " + str(num_resp_flows_matched) + ", num_flows_expected = " + str(num_flows_expected))
524
525
526 def runTest(self):
527 """
528 Run all tests
529 """
530
531 self.test1(False) # Test with no overlaps
532 self.test1(True) # Test with overlaps
533