def get_dependent_edges(self, src_nodes, dst_node): # first, find all nodes that are only connected to two neighbors - any path coming from one neighbor goes to # the other one and means that it could also be modeled by a direct edge between the two neighbors candidate_nodes = list() for node in self.nodes: if node not in src_nodes and node != dst_node and self.in_degree(node) == 2 and self.out_degree(node) == 2: candidate_nodes.append(node) dependent_edges = list() while candidate_nodes: curr_nodes = [candidate_nodes.pop()] curr_edges = set() while curr_nodes: node = curr_nodes.pop() neighbors = self.neighbors(node) for neighbor in neighbors: curr_edges.add(Link.get_name(neighbor, node)) if neighbor in candidate_nodes: candidate_nodes.remove(neighbor) curr_nodes.append(neighbor) dependent_edges.append(list(curr_edges)) return dependent_edges
def get_query(sources): sources = [PolicySource(src_router) for src_router in sources] destinations = PolicyDestination("r12", "FastEthernet1/1", "11.0.12.0/24") num_paths = 3 links = [Link("l{id}".format(id=i), edge, LinkState.UP) for i, edge in enumerate(ResponseTest.get_all_edges())] netenv = NetworkEnvironment(links) return Query(PolicyType.LoadBalancingSimple, sources, destinations, num_paths, netenv, negate=False)
def __str__(self): output = '%d nodes, %d edges:\n' % (len(self.nodes), len(self.edges)) for edge in sorted(self.edges, key=lambda e: e[0]): state = self.edges[edge]['state'] output += '{edge}: {state}'.format(edge=Link.get_name(edge[0], edge[1]), state='up' if state == LinkState.UP else 'down') if 'cost' in self.edges[edge]: cost = self.edges[edge]['cost'] output += ' with cost %d' % (cost, ) output += '\n' return output
def parse_generic_counterexample(message): assert message.startswith("Counterexample") failed_links = set() source_routers = set() edges = re.findall('link\((.+?),(.+?)\)', message) for router1, router2 in edges: failed_links.add(Link.get_name(router1, router2)) return failed_links, source_routers
def get_multi_flow_counter_example(): query = ResponseTest.get_query(sources=["r15", "r6"]) response = ("Flow: ingress:r15 vrf:default 0.0.0.0->11.12.7.2 HOPOPT packetLength:0 state:NEW\n" " environment:BASE\n" "Environment{testrigName=tempSnapshot, edgeBlacklist=[<r11:FastEthernet0/1, r16:FastEthernet1/1>, " "<r12:FastEthernet0/0, r13:FastEthernet1/0>, <r16:FastEthernet0/0, r17:FastEthernet1/0>, " "<r16:FastEthernet0/1, r21:FastEthernet1/1>, <r17:FastEthernet0/1, r22:FastEthernet1/1>, " "<r18:FastEthernet0/1, r23:FastEthernet1/1>, <r19:FastEthernet0/0, r20:FastEthernet1/0>, " "<r2:FastEthernet0/0, r3:FastEthernet1/0>, <r2:FastEthernet1/0, r1:FastEthernet0/0>, " "<r21:FastEthernet0/0, r22:FastEthernet1/0>, <r23:FastEthernet0/0, r24:FastEthernet1/0>, " "<r23:FastEthernet1/0, r22:FastEthernet0/0>, <r25:FastEthernet1/0, r24:FastEthernet0/0>, " "<r3:FastEthernet0/0, r4:FastEthernet1/0>, <r4:FastEthernet0/1, r9:FastEthernet1/1>, " "<r6:FastEthernet0/0, r7:FastEthernet1/0>, <r6:FastEthernet1/1, r1:FastEthernet0/1>, " "<r9:FastEthernet0/1, r14:FastEthernet1/1>], interfaceBlacklist=null, nodeBlacklist=null, " "bgpTables=null, routingTables=null, externalBgpAnnouncements=[]}\n" " Hop 1: r15:FastEthernet1/1 -> r10:FastEthernet0/1\n" " Hop 2: r10:FastEthernet1/0 -> r9:FastEthernet0/0\n" " Hop 3: r9:FastEthernet1/0 -> r8:FastEthernet0/0\n" " Hop 4: r8:FastEthernet1/0 -> r7:FastEthernet0/0\n" " Hop 5: r7:FastEthernet0/1 -> r12:FastEthernet1/1\n" " ACCEPTED\n\n" "Flow: ingress:r6 vrf:default 0.0.0.0->11.12.7.2 HOPOPT packetLength:0 state:NEW\n" " environment:BASE\n" "Environment{testrigName=tempSnapshotId, edgeBlacklist=[" "<r11:FastEthernet0/1, r16:FastEthernet1/1>, <r12:FastEthernet0/0, r13:FastEthernet1/0>, " "<r16:FastEthernet0/0, r17:FastEthernet1/0>, <r16:FastEthernet0/1, r21:FastEthernet1/1>, " "<r17:FastEthernet0/1, r22:FastEthernet1/1>, <r18:FastEthernet0/1, r23:FastEthernet1/1>, " "<r19:FastEthernet0/0, r20:FastEthernet1/0>, <r2:FastEthernet0/0, r3:FastEthernet1/0>, " "<r2:FastEthernet1/0, r1:FastEthernet0/0>, <r21:FastEthernet0/0, r22:FastEthernet1/0>, " "<r23:FastEthernet0/0, r24:FastEthernet1/0>, <r23:FastEthernet1/0, r22:FastEthernet0/0>, " "<r25:FastEthernet1/0, r24:FastEthernet0/0>, <r3:FastEthernet0/0, r4:FastEthernet1/0>, " "<r4:FastEthernet0/1, r9:FastEthernet1/1>, <r6:FastEthernet0/0, r7:FastEthernet1/0>, " "<r6:FastEthernet1/1, r1:FastEthernet0/1>, <r9:FastEthernet0/1, r14:FastEthernet1/1>], " "interfaceBlacklist=null, nodeBlacklist=null, " "bgpTables=null, routingTables=null, externalBgpAnnouncements=[]}\n" " Hop 1: r6:FastEthernet0/1 -> r11:FastEthernet1/1\n" " Hop 2: r11:FastEthernet0/0 -> r12:FastEthernet1/0\n" " ACCEPTED") failed_edges = [("r12", "r13"), ("r16", "r17"), ("r16", "r21"), ("r17", "r22"), ("r18", "r23"), ("r19", "r20"), ("r2", "r3"), ("r2", "r1"), ("r21", "r22"), ("r23", "r24"), ("r23", "r22"), ("r25", "r24"), ("r3", "r4"), ("r4", "r9"), ("r6", "r7"), ("r6", "r1"), ("r9", "r14"), ("r16", "r11"), ] failed_links = set([Link.get_name(r1, r2) for r1, r2 in failed_edges]) src_routers = {"r15", "r6"} return query, response, failed_links, src_routers
def get_random_links(): links = list() symbolic_link_names = list() for i in range(1, 15): r1 = "r{id}".format(id=i) r2 = "r{id}".format(id=i * 9) state = random.choice( [LinkState.UP, LinkState.DOWN, LinkState.SYMBOLIC]) link = Link("l{id}".format(id=i), (r1, r2), state=state) links.append(link) if state == LinkState.SYMBOLIC: symbolic_link_names.append(link.name) max_failures = random.randint(0, len(symbolic_link_names)) return links, symbolic_link_names, max_failures
def build_network(backend, scenario_path, max_failures, waypoints_min, waypoints_fraction): topology_files = backend.get_topology() network = BackendTopologyBuilder.build_topology(topology_files, scenario_path) # get waypoints all_routers = network.nodes() num_waypoints = max(waypoints_min, int(len(all_routers) / waypoints_fraction)) waypoints = random.sample(all_routers, num_waypoints) links = list() all_edges = network.get_undirected_edges() for i, edge in enumerate(all_edges): links.append(Link("l{id}".format(id=i), edge, LinkState.SYMBOLIC)) # create the network environment netenv = NetworkEnvironment(links, k_failures=max_failures) return network, netenv, waypoints
def parse_flow_counterexample(message): assert message.startswith("Flow:") failed_links = set() source_routers = set() first = True for item in re.split('\n\n', message): ingress = re.search('ingress:(.+?) vrf:', item) if ingress and ingress.group(1): source_routers.add(ingress.group(1)) if first: first = True tmp_blacklist = re.search('edgeBlacklist=\[(.*?)\]', item) if tmp_blacklist: blacklist = tmp_blacklist.group(1) edges = re.findall('<(.+?):(.+?),\s(.+?):(.+?)>', blacklist) for router1, intf1, router2, intf2 in edges: failed_links.add(Link.get_name(router1, router2)) return failed_links, source_routers
def test_get_dependent_edges(self): router_pairs = [ ("r1", "r2", [[Link.get_name("r1", "r4"), Link.get_name("r3", "r4")]]), ("r1", "r3", [[Link.get_name("r1", "r4"), Link.get_name("r3", "r4")], [Link.get_name("r1", "r2"), Link.get_name("r2", "r3")]]), ("r2", "r4", []), ] for r1, r2, true_dep_edges in router_pairs: tmp_dependent_edges = self.topology.get_dependent_edges([r1], r2) dependent_edges = [ sorted(dep_edges) for dep_edges in tmp_dependent_edges ] self.assertCountEqual(true_dep_edges, dependent_edges, msg="dependent edges from {} to {}".format( r1, r2))
def get_links(self): if not self.links: for i, edge in enumerate(self.get_undirected_edges()): link_id = 'l%d' % i self.links.append(Link(link_id, edge)) return self.links
def get_next_env(self, fwd_graphs, provided_env=None): # first check if there are any samples left, if not, return None if not self.more_envs(): self.logger.debug("No more states left for sampling") return None # reset all policies on the edges for r1, r2 in self.graph.edges: self.graph.edges[r1, r2]['policies'] = set() # from the policy db get all destinations and the sources for which we have a policy source_counts = self.policy_db.get_source_counts( status=PolicyStatus.UNKNOWN) # create a pseudo policy identifier - at the moment, we don't actually care which policy it represents, but # just that it represents different policies. policy_id = 0 # augment the weight graph with the policy information for subnet, counts in source_counts.items(): assert subnet in fwd_graphs, "no forwarding graph for {subnet}".format( subnet=subnet) fwd_graph = fwd_graphs[subnet] for source, counts in counts.items(): all_paths = nx.all_simple_paths(fwd_graph, source.router, "sink") for count in range(counts): policy_id += 1 for path in all_paths: for i in range(0, len(path) - 2): self.graph.edges[path[i], path[i + 1]]["policies"].add( policy_id) if self.debug: output = "Weighted Graph:\n" for r1, r2, policies in self.graph.edges.data("policies"): # output += "\t{link} - {policies}\n".format(link=Link.get_name(r1, r2), policies=policies) output += "\t{link} - num policies: {policies}\n".format( link=Link.get_name(r1, r2), policies=len(policies)) self.logger.debug(output) tries = 0 excluded_links = set() while True: tries += 1 output = "" failed_links = list() # order the links by the number of policies they "carry" num_edges = len(self.graph.edges) covered_policies = set() i = 0 while len(failed_links) < self.netenv.k_failures and i < num_edges: all_edges = sorted( self.graph.edges.data("policies"), key=lambda x: len(x[2].difference(covered_policies)), reverse=True) r1, r2, policies = all_edges[i] link = Link.get_name(r1, r2) if link not in excluded_links: i = 0 failed_links.append(Link.get_name(r1, r2)) covered_policies |= policies output += "\t{link} - {weight}\n".format( link=Link.get_name(r1, r2), weight=len(policies)) else: i += 1 self.logger.debug("Link choice:\n{links}".format(links=output)) concrete_env = ConcreteEnvironment.from_failed_links( self.links, failed_links) # check if we have already used that specific environment if self.use_env(concrete_env): self.last_sample = concrete_env return concrete_env elif tries > 100 or not failed_links: self.logger.debug( "Couldn't find an unused environment and hence, stop here." ) return None else: # exclude some links excluded_links.add(random.choice(failed_links))
def get_next_env(self, fwd_graphs, provided_env=None): # first check if there are any samples left, if not, return None if not self.more_envs(): self.logger.debug("No more states left for sampling") return None # reset all policies on the edges for r1, r2 in self.graph.edges: self.graph.edges[r1, r2]['policies'] = set() # from the policy db get all destinations and the sources for which we have a policy source_counts = self.policy_db.get_source_counts( status=PolicyStatus.UNKNOWN) # create a pseudo policy identifier - at the moment, we don't actually care which policy it represents, but # just that it represents different policies. policy_id = 0 # augment the weight graph with the policy information for subnet, counts in source_counts.items(): assert subnet in fwd_graphs, "no forwarding graph for {subnet}".format( subnet=subnet) fwd_graph = fwd_graphs[subnet] for source, counts in counts.items(): all_paths = nx.all_simple_paths(fwd_graph, source.router, "sink") for count in range(counts): policy_id += 1 for path in all_paths: for i in range(0, len(path) - 2): self.graph.edges[path[i], path[i + 1]]["policies"].add( policy_id) # Debug if self.debug: output = "Weighted Graph:\n" for r1, r2, policies in self.graph.edges.data("policies"): # output += "\t{link} - {policies}\n".format(link=Link.get_name(r1, r2), policies=policies) output += "\t{link} - num policies: {policies}\n".format( link=Link.get_name(r1, r2), policies=len(policies)) self.logger.debug(output) # Pick the environment - Try at most 100 times tries = 0 while True: tries += 1 output = "" failed_links = list() # order the links by the number of policies they "carry" covered_policies = set() for i in range(0, self.netenv.k_failures): all_edges = list(self.graph.edges.data("policies")) total_weight = 0 weights = list() for r1, r2, policies in all_edges: num_uncovered_policies = len( policies.difference(covered_policies)) total_weight += num_uncovered_policies weights.append(float(num_uncovered_policies)) if total_weight != 0: normalized_weights = [w / total_weight for w in weights] edge_id = np.random.choice(len(all_edges), 1, replace=False, p=normalized_weights)[0] else: edge_id = np.random.choice(len(all_edges), 1, replace=False)[0] r1, r2, policies = all_edges[edge_id] failed_links.append(Link.get_name(r1, r2)) output += "\t{link} - {weight}\n".format(link=Link.get_name( r1, r2), weight=len(policies)) covered_policies |= policies self.logger.debug("Link choice:\n{links}".format(links=output)) concrete_env = ConcreteEnvironment.from_failed_links( self.links, failed_links) # check if we have already used that specific environment if self.use_env(concrete_env): return concrete_env elif tries > 100: self.logger.debug( "Couldn't find an unused environment and hence, stop here." ) return None
def get_generic_single_counter_example(): query = ResponseTest.get_query(sources=["r6"]) response = ("Counterexample Found:\n" "==========================================\n" "Packet:\n" "----------------------\n" "dstIp: 11.12.7.2\n" "\n" "Environment Messages:\n" "----------------------\n" "Final Forwarding:\n" "----------------------\n" "r10,FastEthernet1/0 --> r9,FastEthernet0/0 (OSPF)\n" "r11,FastEthernet0/0 --> r12,FastEthernet1/0 (OSPF)\n" "r13,FastEthernet1/1 --> r8,FastEthernet0/1 (OSPF)\n" "r14,FastEthernet1/0 --> r13,FastEthernet0/0 (OSPF)\n" "r15,FastEthernet1/0 --> r14,FastEthernet0/0 (OSPF)\n" "r15,FastEthernet1/1 --> r10,FastEthernet0/1 (OSPF)\n" "r17,FastEthernet1/1 --> r12,FastEthernet0/1 (OSPF)\n" "r18,FastEthernet1/0 --> r17,FastEthernet0/0 (OSPF)\n" "r19,FastEthernet1/0 --> r18,FastEthernet0/0 (OSPF)\n" "r2,FastEthernet0/1 --> r7,FastEthernet1/1 (OSPF)\n" "r20,FastEthernet1/1 --> r15,FastEthernet0/1 (OSPF)\n" "r24,FastEthernet1/1 --> r19,FastEthernet0/1 (OSPF)\n" "r25,FastEthernet1/1 --> r20,FastEthernet0/1 (OSPF)\n" "r3,FastEthernet0/1 --> r8,FastEthernet1/1 (OSPF)\n" "r4,FastEthernet0/0 --> r5,FastEthernet1/0 (OSPF)\n" "r5,FastEthernet0/1 --> r10,FastEthernet1/1 (OSPF)\n" "r6,FastEthernet0/1 --> r11,FastEthernet1/1 (OSPF)\n" "r7,FastEthernet0/1 --> r12,FastEthernet1/1 (CONNECTED)\n" "r8,FastEthernet1/0 --> r7,FastEthernet0/0 (OSPF)\n" "r9,FastEthernet1/0 --> r8,FastEthernet0/0 (OSPF)\n" "\n" "Failures:\n" "----------------------\n" "link(r1,r2)\n" "link(r1,r6)\n" "link(r11,r16)\n" "link(r12,r13)\n" "link(r14,r9)\n" "link(r16,r17)\n" "link(r16,r21)\n" "link(r17,r22)\n" "link(r18,r23)\n" "link(r19,r20)\n" "link(r2,r3)\n" "link(r21,r22)\n" "link(r22,r23)\n" "link(r23,r24)\n" "link(r24,r25)\n" "link(r3,r4)\n" "link(r4,r9)\n" "link(r6,r7)\n" "==========================================") failed_edges = [("r1", "r2"), ("r1", "r6"), ("r11", "r16"), ("r12", "r13"), ("r14", "r9"), ("r16", "r17"), ("r16", "r21"), ("r17", "r22"), ("r18", "r23"), ("r19", "r20"), ("r2", "r3"), ("r21", "r22"), ("r22", "r23"), ("r23", "r24"), ("r24", "r25"), ("r3", "r4"), ("r4", "r9"), ("r6", "r7")] failed_links = set([Link.get_name(r1, r2) for r1, r2 in failed_edges]) src_routers = set() return query, response, failed_links, src_routers
def get_next_env(self, fwd_graphs, provided_env=None): # first check if there are any samples left, if not, return None if not self.more_envs(): self.logger.debug("No more states left for sampling") return None # make a copy of the weight graph graph = self.graph.copy() # from the policy db get all destinations and the sources for which we have a policy source_counts = self.policy_db.get_source_counts( status=PolicyStatus.UNKNOWN) # augment the weight graph with the policy information for subnet, counts in source_counts.items(): assert subnet in fwd_graphs, "no forwarding graph for {subnet}".format( subnet=subnet) fwd_graph = fwd_graphs[subnet] for source, count in counts.items(): all_paths = nx.all_simple_paths(fwd_graph, source.router, "sink") for path in all_paths: for i in range(0, len(path) - 2): graph.edges[path[i], path[i + 1]]["weight"] += count # order the links by their weight and create the corresponding probability distribution all_edges = sorted(graph.edges.data("weight"), key=lambda x: x[2], reverse=True) if self.debug: output = "Weighted Graph:\n" for r1, r2, weight in all_edges: output += "\t{link} - {weight}\n".format(link=Link.get_name( r1, r2), weight=weight) self.logger.debug(output) total_weight = sum(x[2] for x in all_edges) if total_weight < 1: weights = None else: weights = [float(x[2]) / total_weight for x in all_edges] tries = 0 while True: tries += 1 # find a combination of edges by sampling k edges using the weights try: edge_candidates = np.random.choice(len(all_edges), self.netenv.k_failures, replace=False, p=weights) except ValueError as e: self.logger.error( "There was an error in picking the samples: {error}". format(error=e)) edge_candidates = np.random.choice(len(all_edges), self.netenv.k_failures) # create concrete environment from the edge candidates output = "Link choice:\n" failed_links = list() for edge_id in edge_candidates: r1, r2, weight = all_edges[edge_id] failed_links.append(Link.get_name(r1, r2)) output += "\t{link} - {weight}\n".format(link=Link.get_name( r1, r2), weight=weight) self.logger.debug(output) concrete_env = ConcreteEnvironment.from_failed_links( self.links, failed_links) # check if we have already used that specific environment if self.use_env(concrete_env): return concrete_env elif tries > 100: return self.get_next_unused_env()
def get_next_env(self, fwd_graphs, provided_env=None): # first check if there are any samples left, if not, return None if not self.more_envs(): self.logger.debug("No more states left for sampling") return None # reset all policies on the edges for r1, r2 in self.graph.edges: self.graph.edges[r1, r2]['policies'] = set() # from the policy db get all destinations and the sources for which we have a policy source_counts = self.policy_db.get_source_counts( status=PolicyStatus.UNKNOWN) # create a pseudo policy identifier - at the moment, we don't actually care which policy it represents, but # just that it represents different policies. policy_id = 0 # augment the weight graph with the policy information for subnet, counts in source_counts.items(): assert subnet in fwd_graphs, "no forwarding graph for {subnet}".format( subnet=subnet) fwd_graph = fwd_graphs[subnet] for source, counts in counts.items(): all_paths = nx.all_simple_paths(fwd_graph, source.router, "sink") for count in range(counts): policy_id += 1 for path in all_paths: for i in range(0, len(path) - 2): self.graph.edges[path[i], path[i + 1]]["policies"].add( policy_id) # debug output if self.debug: output = "Weighted Graph:\n" for r1, r2, policies in self.graph.edges.data("policies"): # output += "\t{link} - {policies}\n".format(link=Link.get_name(r1, r2), policies=policies) output += "\t{link} - num policies: {policies}\n".format( link=Link.get_name(r1, r2), policies=len(policies)) self.logger.debug(output) # pick links to fail according to the policies they carry - if we pick an environment that we have used before, # we try to merge the previous environment (which lead us to choose the already used one) and the already # used one to produce a new one. tries = 0 while True: tries += 1 output = "" failed_links = list() # order the links by the number of policies they "carry" covered_policies = set() while len(failed_links) < self.netenv.k_failures: all_edges = sorted( self.graph.edges.data("policies"), key=lambda x: len(x[2].difference(covered_policies)), reverse=True) r1, r2, policies = all_edges[0] link = Link.get_name(r1, r2) failed_links.append(link) covered_policies |= policies output += "\t{link} - {weight}\n".format(link=Link.get_name( r1, r2), weight=len(policies)) self.logger.debug("Link choice:\n{links}".format(links=output)) concrete_env = ConcreteEnvironment.from_failed_links( self.links, failed_links) # check if we have already used that specific environment if self.use_env(concrete_env): self.last_sample = concrete_env return concrete_env elif tries > 100: self.logger.debug( "Couldn't find an unused environment and hence, stop here." ) return None else: self.logger.debug( "We have already used that environment :( Will try to find a new one..." ) links1 = set( link.name for link in self.last_sample.get_links(LinkState.DOWN)) links2 = set(failed_links) # this only works if k is strictly larger than 1 - Hence if k is 0 or 1, there is no other option than # to stop here as we cannot merge anything. if self.netenv.k_failures <= 1: return None failed_links = list() failed_links.append(random.choice(list(links1))) failed_links.append(random.choice(list(links2))) if self.netenv.k_failures - len(failed_links) > 0: union = list(links1.union(links2)) additional_links = random.sample( union, self.netenv.k_failures - len(failed_links)) failed_links.extend(additional_links) output = "" for link in failed_links: output += "\t{link}\n".format(link=link) self.logger.debug("Link choice:\n{links}".format(links=output)) concrete_env = ConcreteEnvironment.from_failed_links( self.links, failed_links) if self.use_env(concrete_env): self.last_sample = concrete_env return concrete_env
def get_loadbalancing_policies(self, policies, forwarding_graphs, dominator_graphs, node_local_reachability=False, simple=True, edge_disjoint=False, node_disjoint=False): start_time = time.time() for subnet, graph in dominator_graphs.items(): all_destinations, destinations, dst_routers = self.get_destination_interfaces_for_subnet( subnet, forwarding_graphs[subnet]) # TODO how to deal with Loadbalancing to same prefix, but last hop is different router?! # only use router specific subnets (e.g., subnets that are connected at a single interface, such as loopback # and host subnets). This removes all subnet that just exist between two routers (usually /31) if len(all_destinations) > 1: continue for node in graph.nodes(): # prevent policies within the same router if node == "sink" or (not node_local_reachability and node in dst_routers): continue if len(destinations[node]) == 1: policy_destination = destinations[node][0] dst_router = policy_destination.router else: self.logger.error( "There should only be a single destination for {node} this subnet: {subnet}." .format(node=node, subnet=subnet)) continue policy_source = PolicySource(node) all_paths = list( nx.all_simple_paths(forwarding_graphs[subnet], node, "sink")) # only continue if there is more than one path available if len(all_paths) > 1: # simple loadbalancing, more than a single path if simple: policies.append((PolicyType.LoadBalancingSimple, policy_destination, len(all_paths), policy_source)) # edge disjoint loadbalancing if edge_disjoint: edge_disjoint_paths = list() edge_union = set() for path in all_paths: edges = set([ Link.get_name(path[i], path[i + 1]) for i in range(len(path) - 1) ]) if not edge_union.intersection(edges): edge_union.union(edges) edge_disjoint_paths.append(path) if len(edge_disjoint_paths) > 1: self.logger.debug( "There are {num_paths} edge-disjoint paths from {src} to {dst}." .format(num_paths=len(edge_disjoint_paths), src=node, dst=dst_router)) # TODO add policies # node disjoint loadbalancing if node_disjoint: node_disjoint_paths = list() node_union = set() for path in all_paths: # need to remove the source and destination as all the paths will have them in common nodes = set(path[1:-1]) if not node_union.intersection(nodes): node_union.union(nodes) node_disjoint_paths.append(path) if len(node_disjoint_paths) > 1: self.logger.debug( "There are {num_paths} node-disjoint paths from {src} to {dst}." .format(num_paths=len(node_disjoint_paths), src=node, dst=dst_router)) # TODO add policies self.logger.debug( "Getting the loadbalancing policies from the forwarding graphs took {time:.4f}s." .format(time=time.time() - start_time, )) return policies