def solve(self, topo, requirement_dags): # a list of tuples with info on the node to be attracted, # the forwarding address, the cost to be set in the fake LSA, # and the respective destinations self.fake_ospf_lsas = [] self.reqs = requirement_dags self.igp_graph = topo self.igp_paths = ssu.all_shortest_paths(self.igp_graph) # process input forwarding DAGs, one at the time for dest, dag in requirement_dags.iteritems(): logger.debug('Solving DAG for dest %s', dest) self.dest, self.dag = dest, dag self.add_dest_to_graphs(dest, dag) if dest not in topo: sinks = dag.predecessors(dest) for s in sinks: logger.info('Adding edge (%s, %s) in the graph', s, self.dest) topo.add_edge(s, dest, weight=self.new_edge_weight) for n in topo.nodes_iter(): if n == dest: # dest is a path in itself self.igp_paths[n] = ([[n]], 0) continue paths = [] cost = sys.maxint for s in sinks: if s not in self.igp_paths[n][0]: # no path to sink continue c = self.igp_paths[n][1][s] p = self.igp_paths[n][0][s] if c < cost: # new spt paths = list(ssu.extend_paths_list(p, dest)) cost = c if c == cost: # ecmp paths.extend(ssu.extend_paths_list(p, dest)) if paths: _t = self.igp_paths[n] _t[0][dest] = paths _t[1][dest] = cost self.complete_dag() # Add temporarily the destination to the igp graph and/or req dags if not self.solvable(dest, dag): continue for node in nx.topological_sort(dag, reverse=True)[1:]: nhs, original_nhs = self.nhs_for(node, dag, dest) if not self.require_fake_node(nhs, original_nhs): logger.debug('%s does not require a fake node (%s - %s)', node, nhs, original_nhs) continue for req_nh in nhs: logger.debug('Placing a fake node for nh %s', req_nh) self.fake_ospf_lsas.append(ssu.LSA(node=node, nh=req_nh, cost=-1, dest=dest)) return self.fake_ospf_lsas
def check_dest(self): """Check that the destination is present in the DAG and the graph""" dest_in_graph = self.dest in self.g log.debug('Checking for %s in the graph: %s', self.dest, dest_in_graph) if not dest_in_graph: log.info('Adding %s in the graph', self.dest) self.g.add_node(self.dest, data=Node()) new_paths = {} new_paths_cost = {n: sys.maxint for n in self.g.nodes_iter()} dest_in_dag = self.dest in self.dag log.debug('Checking for the presence of %s the the DAG: %s', self.dest, dest_in_dag) if not dest_in_dag or not dest_in_graph: if not dest_in_dag: sinks = ssu.find_sink(self.dag) else: sinks = self.dag.predecessors(self.dest) for s in sinks: if not dest_in_dag: log.info('Adding %s to %s in the dag', self.dest, s) self.dag.add_edge(s, self.dest) if not dest_in_graph: log.info('Adding edge (%s, %s) in the graph', s, self.dest) self.g.add_edge(s, self.dest, weight=self.new_edge_weight) log.debug('Updating spt/cost accordingly') for n in self.g.nodes_iter(): if n == self.dest: new_paths[n] = [[n]] new_paths_cost[n] = 0 continue if not self.has_path(n, s): continue ns_cost = self.cost(n, s) + self.new_edge_weight if ns_cost < new_paths_cost[n]: # Created a new SP ns_path = self.path(n, s) new_paths_cost[n] = ns_cost new_paths[n] = list(ssu.extend_paths_list(ns_path, self.dest )) elif ns_cost == new_paths_cost: # Created ECMP ns_path = self.path(n, s) new_paths[n].extend(ssu.extend_paths_list(ns_path, self.dest )) for n, p in new_paths.iteritems(): # Incrementally update the SPT self.register_path(n, self.dest, p, new_paths_cost[n])