| """ |
| Flow query test case. |
| |
| Attempts to fill switch to capacity with randomized flows, and ensure that they all are read back correctly. |
| """ |
| |
| import logging |
| |
| import unittest |
| import random |
| |
| import oftest.controller as controller |
| import oftest.cstruct as ofp |
| import oftest.message as message |
| import oftest.dataplane as dataplane |
| import oftest.action as action |
| import oftest.action_list as action_list |
| import oftest.parse as parse |
| import pktact |
| import basic |
| |
| from testutils import * |
| from time import sleep |
| |
| #@var port_map Local copy of the configuration map from OF port |
| # numbers to OS interfaces |
| pa_port_map = None |
| #@var pa_logger Local logger object |
| pa_logger = None |
| #@var pa_config Local copy of global configuration data |
| pa_config = None |
| |
| def test_set_init(config): |
| """ |
| Set up function for packet action test classes |
| |
| @param config The configuration dictionary; see oft |
| """ |
| |
| basic.test_set_init(config) |
| |
| global pa_port_map |
| global pa_logger |
| global pa_config |
| |
| pa_logger = logging.getLogger("pkt_act") |
| pa_logger.info("Initializing test set") |
| pa_port_map = config["port_map"] |
| pa_config = config |
| |
| |
| def shuffle(list): |
| n = len(list) |
| lim = n * n |
| i = 0 |
| while i < lim: |
| a = random.randint(0, n - 1) |
| b = random.randint(0, n - 1) |
| temp = list[a] |
| list[a] = list[b] |
| list[b] = temp |
| i = i + 1 |
| return list |
| |
| |
| def rand_dl_addr(): |
| 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)] |
| |
| |
| def rand_nw_addr(): |
| return random.randint(0, (1 << 32) - 1) |
| |
| |
| # TBD - These don't belong here |
| |
| all_wildcards = [ofp.OFPFW_IN_PORT, \ |
| ofp.OFPFW_DL_VLAN, \ |
| ofp.OFPFW_DL_SRC, \ |
| ofp.OFPFW_DL_DST, \ |
| ofp.OFPFW_DL_TYPE, \ |
| ofp.OFPFW_NW_PROTO, \ |
| ofp.OFPFW_TP_SRC, \ |
| ofp.OFPFW_TP_DST, \ |
| ofp.OFPFW_NW_SRC_ALL, \ |
| ofp.OFPFW_NW_DST_ALL, \ |
| ofp.OFPFW_DL_VLAN_PCP, \ |
| ofp.OFPFW_NW_TOS \ |
| ] |
| |
| all_actions = [ofp.OFPAT_OUTPUT, |
| ofp.OFPAT_SET_VLAN_VID, |
| ofp.OFPAT_SET_VLAN_PCP, |
| ofp.OFPAT_STRIP_VLAN, |
| ofp.OFPAT_SET_DL_SRC, |
| ofp.OFPAT_SET_DL_DST, |
| ofp.OFPAT_SET_NW_SRC, |
| ofp.OFPAT_SET_NW_DST, |
| ofp.OFPAT_SET_NW_TOS, |
| ofp.OFPAT_SET_TP_SRC, |
| ofp.OFPAT_SET_TP_DST, |
| ofp.OFPAT_ENQUEUE |
| ] |
| |
| |
| class flow_cfg: |
| # Members: |
| # - match |
| # - idle_timeout |
| # - hard_timeout |
| # - priority |
| # - action_list |
| |
| def __init__(self): |
| self.match = parse.ofp_match() |
| self.match.wildcards = ofp.OFPFW_ALL |
| self.idle_timeout = 0 |
| self.hard_timeout = 0 |
| self.priority = 0 |
| self.actions = action_list.action_list() |
| |
| def __eq__(self, x): |
| if self.match != x.match: |
| return False |
| if self.idle_timeout != x.idle_timeout: |
| return False |
| if self.hard_timeout != x.hard_timeout: |
| return False |
| if self.priority != x.priority: |
| return False |
| return self.actions == x.actions # N.B. Action lists are ordered |
| |
| def rand(self, valid_wildcards, valid_actions, valid_ports): |
| # TBD - Are IP addr wildcard specs all or nothing, valid wildcard reported as all 1s or all 0s? |
| self.match.wildcards = valid_wildcards |
| if (self.match.wildcards & ofp.OFPFW_NW_SRC_MASK) == ofp.OFPFW_NW_SRC_MASK: |
| self.match.wildcards = (self.match.wildcards & ~ofp.OFPFW_NW_SRC_MASK) | ofp.OFPFW_NW_SRC_ALL |
| if (self.match.wildcards & ofp.OFPFW_NW_DST_MASK) == ofp.OFPFW_NW_DST_MASK: |
| self.match.wildcards = (self.match.wildcards & ~ofp.OFPFW_NW_DST_MASK) | ofp.OFPFW_NW_DST_ALL |
| |
| exact = True if random.randint(1, 100) == 1 else False |
| |
| for w in all_wildcards: |
| if not exact and (w & valid_wildcards) != 0: |
| if random.randint(1, 100) <= 50: |
| continue |
| |
| if w == ofp.OFPFW_IN_PORT: |
| self.match.in_port = valid_ports[random.randint(0, len(valid_ports) - 1)].port_no |
| self.match.wildcards = self.match.wildcards & ~w |
| elif w == ofp.OFPFW_DL_VLAN: |
| self.match.vl_vlan = random.randint(1, 4094) |
| self.match.wildcards = self.match.wildcards & ~w |
| elif w == ofp.OFPFW_DL_SRC: |
| self.match.dl_src = rand_dl_addr() |
| self.match.wildcards = self.match.wildcards & ~w |
| elif w == ofp.OFPFW_DL_DST: |
| self.match.dl_dst = rand_dl_addr() |
| self.match.wildcards = self.match.wildcards & ~w |
| elif w == ofp.OFPFW_DL_TYPE: |
| if (self.match.wildcards & w) != 0: |
| self.match.dl_type = random.randint(0, (1 << 16) - 1) |
| self.match.wildcards = self.match.wildcards & ~w |
| elif w == ofp.OFPFW_NW_PROTO: |
| if (self.match.wildcards & w) != 0: |
| self.match.nw_proto = random.randint(0, (1 << 8) - 1) |
| self.match.wildcards = self.match.wildcards & ~w |
| self.match.dl_type = 0x0800 |
| self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE |
| elif w == ofp.OFPFW_TP_SRC: |
| self.match.tp_src = random.randint(0, (1 << 16) - 1) |
| self.match.wildcards = self.match.wildcards & ~w |
| self.match.nw_proto = [1, 6, 17][random.randint(0, 2)] |
| self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_NW_PROTO |
| self.match.dl_type = 0x0800 |
| self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE |
| elif w == ofp.OFPFW_TP_DST: |
| self.match.tp_dst = random.randint(0, (1 << 16) - 1) |
| self.match.wildcards = self.match.wildcards & ~w |
| self.match.nw_proto = [1, 6, 17][random.randint(0, 2)] |
| self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_NW_PROTO |
| self.match.dl_type = 0x0800 |
| self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE |
| elif w == ofp.OFPFW_NW_SRC_MASK: |
| n = 0 if exact else random.randint(0, 31) |
| self.match.nw_src = rand_nw_addr() & ~((1 << n) - 1) |
| self.match.wildcards = (self.match.wildcards & ~w) | (n << ofp.OFPFW_NW_SRC_SHIFT) |
| self.match.dl_type = [0x0800, 0x0806][random.randint(0, 1)] |
| self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE |
| elif w == ofp.OFPFW_NW_DST_MASK: |
| n = 0 if exact else random.randint(0, 31) |
| self.match.nw_dst = rand_nw_addr() & ~((1 << n) - 1) |
| self.match.wildcards = (self.match.wildcards & ~w) | (n << ofp.OFPFW_NW_DST_SHIFT) |
| self.match.dl_type = [0x0800, 0x0806][random.randint(0, 1)] |
| self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE |
| elif w == ofp.OFPFW_DL_VLAN_PCP: |
| self.match.dl_vlan_pcp = random.randint(0, (1 << 3) - 1) |
| self.match.wildcards = self.match.wildcards & ~w |
| elif w == ofp.OFPFW_NW_TOS: |
| while True: |
| self.match.nw_tos = random.randint(0, (1 << 8) - 1) |
| if (self.match.nw_tos & 3) == 0: |
| break |
| self.match.wildcards = self.match.wildcards & ~w |
| self.match.dl_type = 0x0800 |
| self.match.wildcards = self.match.wildcards & ~ofp.OFPFW_DL_TYPE |
| |
| # N.B. Don't make the timeout too short, else the flow might disappear before we |
| # get a chance to check for it. |
| t = random.randint(0, 65535) |
| self.idle_timeout = 0 if t < 60 else t |
| t = random.randint(0, 65535) |
| self.hard_timeout = 0 if t < 60 else t |
| self.priority = 65535 if exact else random.randint(1, 65535) |
| |
| # N.B. Action lists are ordered |
| supported_action_idxs = [] |
| ai = 0 |
| while ai < len(all_actions): |
| if ((1 << all_actions[ai]) & valid_actions) != 0: |
| supported_action_idxs.append(ai) |
| ai = ai + 1 |
| |
| supported_action_idxs = shuffle(supported_action_idxs) |
| supported_action_idxs = supported_action_idxs[0 : random.randint(1, len(supported_action_idxs) - 1)] |
| |
| self.actions = action_list.action_list() |
| for ai in supported_action_idxs: |
| a = all_actions[ai] |
| |
| if a == ofp.OFPAT_OUTPUT: |
| # TBD - Output actions are clustered in list, spread them out? |
| port_idxs = shuffle(range(len(valid_ports))) |
| port_idxs = port_idxs[0 : random.randint(1, len(valid_ports) - 1)] |
| for pi in port_idxs: |
| act = action.action_output() |
| act.port = valid_ports[pi].port_no |
| self.actions.add(act) |
| elif a == ofp.OFPAT_SET_VLAN_VID: |
| act = action.action_set_vlan_vid() |
| act.vlan_vid = random.randint(1, 4094) |
| self.actions.add(act) |
| elif a == ofp.OFPAT_SET_VLAN_PCP: |
| # TBD - Temporaily removed, broken in Indigo |
| #act = action.action_set_vlan_pcp() |
| #act.vlan_pcp = random.randint(0, (1 << 3) - 1) |
| pass |
| elif a == ofp.OFPAT_STRIP_VLAN: |
| act = action.action_strip_vlan() |
| self.actions.add(act) |
| elif a == ofp.OFPAT_SET_DL_SRC: |
| act = action.action_set_dl_src() |
| act.dl_addr = rand_dl_addr() |
| self.actions.add(act) |
| elif a == ofp.OFPAT_SET_DL_DST: |
| act = action.action_set_dl_dst() |
| act.dl_addr = rand_dl_addr() |
| self.actions.add(act) |
| elif a == ofp.OFPAT_SET_NW_SRC: |
| act = action.action_set_nw_src() |
| act.nw_addr = rand_nw_addr() |
| self.actions.add(act) |
| elif a == ofp.OFPAT_SET_NW_DST: |
| act = action.action_set_nw_dst() |
| act.nw_addr = rand_nw_addr() |
| self.actions.add(act) |
| elif a == ofp.OFPAT_SET_NW_TOS: |
| act = action.action_set_nw_tos() |
| act.nw_tos = random.randint(0, (1 << 8) - 1) |
| self.actions.add(act) |
| elif a == ofp.OFPAT_SET_TP_SRC: |
| act = action.action_set_tp_src() |
| act.tp_port = random.randint(0, (1 << 16) - 1) |
| self.actions.add(act) |
| elif a == ofp.OFPAT_SET_TP_DST: |
| act = action.action_set_tp_dst() |
| act.tp_port = random.randint(0, (1 << 16) - 1) |
| self.actions.add(act) |
| elif a == ofp.OFPAT_ENQUEUE: |
| # TBD - Enqueue actions are clustered in list, spread them out? |
| port_idxs = shuffle(range(len(valid_ports))) |
| port_idxs = port_idxs[0 : random.randint(1, len(valid_ports) - 1)] |
| for pi in port_idxs: |
| act = action.action_enqueue() |
| act.port = valid_ports[pi].port_no |
| # TBD - Limits for queue number? |
| act.queue_id = random.randint(0, 7) |
| self.actions.add(act) |
| |
| return self |
| |
| def overlap(self, x): |
| if self.priority != x.priority: |
| return False |
| 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: |
| return False |
| 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: |
| return False |
| 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: |
| return False |
| 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: |
| return False |
| 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: |
| return False |
| 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: |
| return False |
| 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: |
| return False |
| 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: |
| return False |
| na = (self.match.wildcards & ofp.OFPFW_NW_SRC_MASK) >> ofp.OFPFW_NW_SRC_SHIFT |
| nb = (x.match.wildcards & ofp.OFPFW_NW_SRC_MASK) >> ofp.OFPFW_NW_SRC_SHIFT |
| if (na < 32 and nb < 32): |
| m = ~((1 << na) - 1) & ~((1 << nb) - 1) |
| if (self.match.nw_src & m) != (x.match.nw_src & m): |
| return False |
| na = (self.match.wildcards & ofp.OFPFW_NW_DST_MASK) >> ofp.OFPFW_NW_DST_SHIFT |
| nb = (x.match.wildcards & ofp.OFPFW_NW_DST_MASK) >> ofp.OFPFW_NW_DST_SHIFT |
| if (na < 32 and nb < 32): |
| m = ~((1 << na) - 1) & ~((1 << nb) - 1) |
| if (self.match.nw_dst & m) != (x.match.nw_dst & m): |
| return False |
| 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: |
| return False |
| 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: |
| return False |
| return True |
| |
| def to_flow_mod_msg(self, msg): |
| msg.match = self.match |
| msg.idle_timeout = self.idle_timeout |
| msg.hard_timeout = self.hard_timeout |
| msg.priority = self.priority |
| msg.actions = self.actions |
| return msg |
| |
| def from_flow_stat(self, msg): |
| self.match = msg.match |
| self.idle_timeout = msg.idle_timeout |
| self.hard_timeout = msg.hard_timeout |
| self.priority = msg.priority |
| self.actions = msg.actions |
| |
| |
| class FlowQuery(basic.SimpleProtocol): |
| """ |
| """ |
| |
| def test1(self, overlapf): |
| """ |
| """ |
| |
| # Clear all flows from switch |
| self.logger.debug("Deleting all flows from switch") |
| rc = delete_all_flows(self.controller, pa_logger) |
| self.assertEqual(rc, 0, "Failed to delete all flows") |
| |
| # Get valid port numbers |
| # Get number of tables supported |
| # Get actions supported by switch |
| |
| self.logger.debug("Retrieving features from switch") |
| request = message.features_request() |
| (sw_features, pkt) = self.controller.transact(request, timeout=2) |
| self.assertTrue(sw_features is not None, "No reply to features_request") |
| self.logger.debug("Switch features -") |
| self.logger.debug("Number of tables: " + str(sw_features.n_tables)) |
| self.logger.debug("Supported actions: " + hex(sw_features.actions)) |
| self.logger.debug("Ports: " + str(map(lambda x: x.port_no, sw_features.ports))) |
| |
| # For each table, get wildcards supported maximum number of flows |
| |
| self.logger.debug("Retrieving table stats from switch") |
| request = message.table_stats_request() |
| (tbl_stats, pkt) = self.controller.transact(request, timeout=2) |
| self.assertTrue(tbl_stats is not None, "No reply to table_stats_request") |
| active_count = 0 |
| tbl_idx = 0 |
| while tbl_idx < sw_features.n_tables: |
| self.logger.debug("Table " + str(tbl_idx) + " - ") |
| self.logger.debug("Supported wildcards: " + hex(tbl_stats.stats[tbl_idx].wildcards)) |
| self.logger.debug("Max entries: " + str(tbl_stats.stats[tbl_idx].max_entries)) |
| self.logger.debug("Active count: " + str(tbl_stats.stats[tbl_idx].active_count)) |
| active_count = active_count + tbl_stats.stats[tbl_idx].active_count |
| tbl_idx = tbl_idx + 1 |
| |
| self.assertEqual(active_count, 0, "Total number of active entries not 0 -- delete all flow failed?") |
| |
| |
| # For each table, think up flows to fill it |
| |
| self.logger.debug("Creating flows") |
| num_flows = 0 |
| num_overlaps = 0 |
| tbl_flows = [] |
| tbl_idx = 0 |
| while tbl_idx < sw_features.n_tables: |
| flow_cfgs = [] |
| flow_idx = 0 |
| while flow_idx < tbl_stats.stats[tbl_idx].max_entries: |
| flow_out = flow_cfg().rand(tbl_stats.stats[tbl_idx].wildcards, sw_features.actions, sw_features.ports) |
| j = 0 |
| while j < len(flow_cfgs): |
| if flow_out.overlap(flow_cfgs[j]): |
| break |
| j = j + 1 |
| if j < len(flow_cfgs): |
| num_overlaps = num_overlaps + 1 |
| flow_out.overlap = True |
| else: |
| flow_out.overlap = False |
| flow_cfgs.append(flow_out) |
| num_flows = num_flows + 1 |
| flow_idx = flow_idx + 1 |
| tbl_flows.append(flow_cfgs) |
| tbl_idx = tbl_idx + 1 |
| |
| self.logger.debug("Created " + str(num_flows) + " flows, with " + str(num_overlaps) + " overlaps") |
| |
| # Send all flows to switch |
| |
| self.logger.debug("Sending flows to switch") |
| flow_num = 1 |
| tbl_idx = 0 |
| while tbl_idx < sw_features.n_tables: |
| flow_idx = 0 |
| while flow_idx < len(tbl_flows[tbl_idx]): |
| self.logger.debug("Sending flow number " + str(flow_num)) |
| flow_mod_msg = message.flow_mod() |
| flow_mod_msg.buffer_id = 0xffffffff |
| flow_mod_msg.cookie = random.randint(0, (1 << 53) - 1) |
| tbl_flows[tbl_idx][flow_idx].to_flow_mod_msg(flow_mod_msg) |
| if overlapf: |
| flow_mod_msg.flags = flow_mod_msg.flags | ofp.OFPFF_CHECK_OVERLAP |
| rv = self.controller.message_send(flow_mod_msg) |
| self.assertTrue(rv != -1, "Error installing flow mod") |
| (errmsg, pkt) = self.controller.poll(ofp.OFPT_ERROR, 1) # TBD - Tune timeout for error message |
| if errmsg is not None: |
| # Got ERROR message |
| if errmsg.type == ofp.OFPET_FLOW_MOD_FAILED and errmsg.code == ofp.OFPFMFC_OVERLAP: |
| # Got "overlap" ERROR message |
| self.assertTrue(overlapf and tbl_flows[tbl_idx][flow_idx].overlap, "Got unexpected OVERLAP error message") |
| else: |
| self.assertTrue(False, "Got unexpected error message, type = " + str(errmsg.type) + ", code = " + str(errmsg.code)) |
| else: |
| # Did not get ERROR message |
| self.assertTrue(not (overlapf and tbl_flows[tbl_idx][flow_idx].overlap), "Did not get expected OVERLAP message") |
| |
| flow_idx = flow_idx + 1 |
| flow_num = flow_num + 1 |
| tbl_idx = tbl_idx + 1 |
| |
| # Send barrier, to make sure all flows are in |
| |
| barrier = message.barrier_request() |
| (resp, pkt) = self.controller.transact(barrier, 5) |
| self.assertTrue(resp is not None, "Did not receive response to barrier request") |
| |
| # Check number of flows reported in table stats |
| |
| self.logger.debug("Verifying that table stats reports the correct number of active flows") |
| request = message.table_stats_request() |
| (tbl_stats_after, pkt) = self.controller.transact(request, timeout=2) |
| self.assertTrue(tbl_stats_after is not None, "No reply to table_stats_request") |
| |
| num_flows_reported = 0 |
| for ts in tbl_stats_after.stats: |
| num_flows_reported = num_flows_reported + ts.active_count |
| |
| num_flows_expected = num_flows |
| if overlapf: |
| num_flows_expected = num_flows_expected - num_overlaps |
| |
| 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)) |
| |
| # Retrieve all flows from switch |
| |
| self.logger.debug("Retrieving all flows from switch") |
| stat_req = message.flow_stats_request() |
| query_match = ofp.ofp_match() |
| query_match.wildcards = ofp.OFPFW_ALL |
| stat_req.match = query_match |
| stat_req.table_id = 0xff |
| stat_req.out_port = ofp.OFPP_NONE; |
| flow_stats, pkt = self.controller.transact(stat_req, timeout=2) |
| self.assertTrue(flow_stats is not None, "Get all flow stats failed") |
| |
| # Verify retrieved flows |
| |
| self.logger.debug("Verifying retrieved flows") |
| |
| self.assertEqual(flow_stats.type, ofp.OFPST_FLOW, "Unexpected type of response message") |
| |
| num_flows_reported = len(flow_stats.stats) |
| |
| 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)) |
| |
| tbl_idx = 0 |
| while tbl_idx < sw_features.n_tables: |
| flow_idx = 0 |
| while flow_idx < len(tbl_flows[tbl_idx]): |
| tbl_flows[tbl_idx][flow_idx].resp_matched = False |
| flow_idx = flow_idx + 1 |
| tbl_idx = tbl_idx + 1 |
| |
| num_resp_flows_matched = 0 |
| for flow_stat in flow_stats.stats: |
| flow_in = flow_cfg() |
| flow_in.from_flow_stat(flow_stat) |
| |
| tbl_idx = 0 |
| while tbl_idx < sw_features.n_tables: |
| flow_idx = 0 |
| while flow_idx < len(tbl_flows[tbl_idx]): |
| f = tbl_flows[tbl_idx][flow_idx] |
| if not f.resp_matched and (not overlapf or not f.overlap) and f == flow_in: |
| f.resp_matched = True |
| num_resp_flows_matched = num_resp_flows_matched + 1 |
| break |
| flow_idx = flow_idx + 1 |
| self.assertTrue(flow_idx < len(tbl_flows[tbl_idx]), "Reponse flow does not match any configured flow") |
| tbl_idx = tbl_idx + 1 |
| |
| 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)) |
| |
| |
| def runTest(self): |
| """ |
| Run all tests |
| """ |
| |
| self.test1(False) # Test with no overlaps |
| self.test1(True) # Test with overlaps |
| |