Пример #1
0
 def proxy_add(self, points):
     """
     :param points: (source, fwd, cost, prefix)*
     """
     log.info('Shapeshifter added attraction points: %s', points)
     for prefix, route in self._get_proxy_routes(points):
         self.install_route(prefix, route, self.leader)
Пример #2
0
 def update_graph(self, new_graph):
     self.leader_watchdog.check_leader(self.get_leader())
     added_edges = new_graph.difference(self.graph)
     removed_edges = self.graph.difference(new_graph)
     node_prop_diff = {n: data
                       for n, data in new_graph.nodes_iter(data=True)
                       if n not in self.graph or
                       (data.viewitems() - self.graph.node[n].viewitems())}
     # Propagate differences
     if added_edges or removed_edges or node_prop_diff:
         log.debug('Pushing changes')
         for u, v in added_edges:
             self.for_all_listeners('add_edge', u, v,
                                    new_graph.export_edge_data(u, v))
         for u, v in removed_edges:
             self.for_all_listeners('remove_edge', u, v)
         if node_prop_diff:
             self.for_all_listeners('update_node_properties',
                                    **node_prop_diff)
         if CFG.getboolean(DEFAULTSECT, 'draw_graph'):
             new_graph.draw(CFG.get(DEFAULTSECT, 'graph_loc'))
         self.graph = new_graph
         log.info('LSA update yielded +%d -%d edges changes, '
                   '%d node property changes', len(added_edges),
                   len(removed_edges), len(node_prop_diff))
         self.for_all_listeners('commit')
Пример #3
0
 def proxy_add(self, points):
     """
     :param points: (source, fwd, cost, prefix)*
     """
     log.info('Shapeshifter added attraction points: %s', points)
     for prefix, route in self._get_proxy_routes(points):
         self.install_route(prefix, route, self.leader)
Пример #4
0
 def proxy_remove(self, points):
     """
     :param points: (source, fwd, cost, prefix)*
     """
     log.info('Shapeshifter removed attraction points: %s', points)
     for prefix, route in self._get_proxy_routes(points):
         # We don't need the cost
         self.remove_route_part(prefix, self.leader, *(r[0] for r in route))
Пример #5
0
 def proxy_remove(self, points):
     """
     :param points: (source, fwd, cost, prefix)*
     """
     log.info('Shapeshifter removed attraction points: %s', points)
     for prefix, route in self._get_proxy_routes(points):
         # We don't need the cost
         self.remove_route_part(prefix, self.leader, *(r[0] for r in route))
Пример #6
0
 def check_leader(self, instance):
     was_leader = self.leader
     self.leader = self.instance == self.lsdb.get_leader()
     if self.leader and not was_leader:  # We are the new leader
         log.info('Elected as leader')
         for route in self.routes.values():
             route.advertize()
     elif was_leader and not self.leader:
         log.info('No longer leader')
Пример #7
0
 def _get_diff_lsas(self):
     self._refresh_augmented_topo()
     new_lsas = set(self.optimizer.get_fake_lsas())
     log.info('New LSA set: %s', new_lsas)
     to_add = new_lsas.difference(self.current_lsas)
     to_rem = self.current_lsas.difference(new_lsas)
     log.info('Removing LSA set: %s', to_rem)
     self.current_lsas = new_lsas
     return to_add, to_rem
Пример #8
0
 def check_leader(self, instance):
     was_leader = self.leader
     self.leader = self.instance == self.lsdb.get_leader()
     if self.leader and not was_leader:  # We are the new leader
         log.info('Elected as leader')
         for route in self.routes.values():
             route.advertize()
     elif was_leader and not self.leader:
         log.info('No longer leader')
Пример #9
0
 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
Пример #10
0
 def retract(self, address, advertize):
     try:
         point = self.attraction_points.pop(address)
         if advertize:
             point.retract(self.prefix)
         return point.node
     except KeyError:
         log.info('Unkown attraction point %s for prefix %s',
                  address, self.prefix)
         return None
Пример #11
0
    def remove_session_routes(self, session=None):
        """Removes the routes that were forced by the application in the session.
        This function is ment to be invoked when application disconnects, but for the moment we will
        use it in self.proxy_connected

        TODO: Keep track of which routes were forced by the application in the current session
        TODO: Make this function be invoked in the SJMPServer when the application disconnects
        """
        log.info("Removing routes from current session...")
        map(self.remove_route, self.routes.keys())
Пример #12
0
 def register_change_listener(self, listener):
     try:
         del self.listener[listener]
         log.info('Shapeshifter disconnected.')
     except KeyError:
         log.info('Shapeshifter connected.')
         l = ProxyCloner(ShapeshifterProxy, listener)
         self.listener[listener] = l
         l.boostrap_graph(graph=[(u, v, d.get('metric', -1))
                                 for u, v, d in self.graph.edges(data=True)])
Пример #13
0
 def create_node(self, id=None, cls=Router):
     """
     Create a new node
     :param id: The name of the new node
     :param cls: The class to use to instantiate it
     """
     n = cls(id=id, prefix=self.name, namespaced=True)
     self.nodes[n.id] = n
     log.info('Created node %s', n.id)
     return n
 def refresh_augmented_topo(self):
     log.info('Solving topologies')
     try:
         self.optimizer.solve(self.igp_graph,
                              self.fwd_dags)
     except Exception as e:
         log.exception(e)
         return self.advertized_lsa
     else:
         return set(self.optimizer.get_fake_lsas())
Пример #15
0
 def create_node(self, id=None, cls=Router):
     """
     Create a new node
     :param id: The name of the new node
     :param cls: The class to use to instantiate it
     """
     n = cls(id=id, prefix=self.name, namespaced=True)
     self.nodes[n.id] = n
     log.info('Created node %s', n.id)
     return n
Пример #16
0
 def refresh_augmented_topo(self):
     log.info('Solving topologies')
     if not self.json_proxy.alive() or not self.has_initial_topo:
         log.debug('Skipping as we do not yet have a topology')
         return self.advertized_lsa
     try:
         self.optimizer.solve(self.igp_graph, self.fwd_dags)
     except Exception as e:
         log.exception(e)
         return self.advertized_lsa
     else:
         return set(self.optimizer.get_fake_lsas())
Пример #17
0
 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 = ShortestPath(self.igp_graph)
     # process input forwarding DAGs, one at the time
     for dest, dag in requirement_dags.iteritems():
         log.info('Solving DAG for dest %s', dest)
         self.dest, self.dag = dest, dag
         log.debug('Checking dest in dag')
         ssu.add_dest_to_graph(dest, dag)
         log.debug('Checking dest in igp graph')
         ssu.add_dest_to_graph(dest,
                               topo,
                               edges_src=dag.predecessors,
                               spt=self.igp_paths,
                               metric=self.new_edge_metric)
         ssu.complete_dag(dag,
                          topo,
                          dest,
                          self.igp_paths,
                          skip=self.reqs.keys())
         # Add temporarily the destination to the igp graph and/or req dags
         if not ssu.solvable(dag, topo):
             log.warning('Skipping requirement for dest: %s', dest)
             continue
         for node in dag:
             nhs = self.nhs_for(node, dest, dag)
             if not nhs:
                 continue
             for req_nh in nhs:
                 log.debug('Placing a fake node for %s->%s', node, req_nh)
                 for i in xrange(get_edge_multiplicity(dag, node, req_nh)):
                     self.fake_ospf_lsas.append(
                         ssu.LSA(node=node,
                                 nh=req_nh,
                                 cost=(-1 - i),
                                 dest=dest))
         # Check whether we need to include one more fake node to handle
         # the case where we create a new route from scratch.
         for p in dag.predecessors_iter(dest):
             if not is_fake(topo, p, dest):
                 continue
             log.debug(
                 '%s is a terminal node towards %s but had no prior '
                 'route to it! Adding a synthetic route', p, dest)
             self.fake_ospf_lsas.append(
                 ssu.GlobalLie(dest, self.new_edge_metric, p))
     return self.fake_ospf_lsas
 def refresh_augmented_topo(self):
     log.info('Solving topologies')
     if not self.json_proxy.alive() or not self.has_initial_topo:
         log.debug('Skipping as we do not yet have a topology')
         return self.advertized_lsa
     try:
         self.optimizer.solve(self.igp_graph,
                              self.fwd_dags)
     except Exception as e:
         log.exception(e)
         return self.advertized_lsa
     else:
         return set(self.optimizer.get_fake_lsas())
Пример #19
0
 def register_change_listener(self, listener):
     try:
         del self.listener[listener]
         log.info('Shapeshifter disconnected.')
     except KeyError:
         log.info('Shapeshifter connected.')
         l = ProxyCloner(ShapeshifterProxy, listener)
         self.listener[listener] = l
         l.bootstrap_graph(graph=[(u, v, d)
                                  for u, v, d in self.graph.export_edges()
                                  ],
                           node_properties={n: data for n, data in
                                            self.graph.nodes_iter(data=True)
                                            })
Пример #20
0
 def update_graph(self, new_graph):
     added_edges = graph_diff(new_graph, self.graph)
     removed_edges = graph_diff(self.graph, new_graph)
     # Propagate differences
     if len(added_edges) > 0 or len(removed_edges) > 0:
         log.debug('Pushing changes')
         for u, v in added_edges:
             self.listener_add_edge(u, v, new_graph[u][v]['metric'])
         for u, v in removed_edges:
             self.listener_remove_edge(u, v)
         if CFG.getboolean(DEFAULTSECT, 'draw_graph'):
             draw_graph(new_graph)
         self.graph = new_graph
         log.info('LSA update yielded +%d -%d edges changes' %
                  (len(added_edges), len(removed_edges)))
Пример #21
0
 def print_routes(self):
     """
     Log all fibbing routes that are installed in the network
     """
     log.info('----------------------')
     log.info('%s fibbing routes', len(self.routes))
     log.info('----------------------')
     for route in self.routes.values():
         log.info(route)
Пример #22
0
 def print_routes(self):
     """
     Log all fibbing routes that are installed in the network
     """
     log.info('----------------------')
     log.info('%s fibbing routes', len(self.routes))
     log.info('----------------------')
     for route in self.routes.values():
         log.info(route)
Пример #23
0
 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 = ShortestPath(self.igp_graph)
     # process input forwarding DAGs, one at the time
     for dest, dag in requirement_dags.iteritems():
         log.info('Solving DAG for dest %s', dest)
         self.dest, self.dag = dest, dag
         log.debug('Checking dest in dag')
         ssu.add_dest_to_graph(dest, dag)
         log.debug('Checking dest in igp graph')
         ssu.add_dest_to_graph(dest, topo,
                               edges_src=dag.predecessors,
                               spt=self.igp_paths,
                               metric=self.new_edge_metric)
         ssu.complete_dag(dag, topo, dest, self.igp_paths,
                          skip=self.reqs.keys())
         # Add temporarily the destination to the igp graph and/or req dags
         if not ssu.solvable(dag, topo):
             log.warning('Skipping requirement for dest: %s', dest)
             continue
         for node in dag:
             nhs = self.nhs_for(node, dest, dag)
             if not nhs:
                 continue
             for req_nh in nhs:
                 log.debug('Placing a fake node for %s->%s', node, req_nh)
                 for i in xrange(get_edge_multiplicity(dag, node, req_nh)):
                     self.fake_ospf_lsas.append(ssu.LSA(node=node,
                                                        nh=req_nh,
                                                        cost=(-1 - i),
                                                        dest=dest))
         # Check whether we need to include one more fake node to handle
         # the case where we create a new route from scratch.
         for p in dag.predecessors_iter(dest):
             if not is_fake(topo, p, dest):
                 continue
             log.debug('%s is a terminal node towards %s but had no prior '
                       'route to it! Adding a synthetic route', p, dest)
             self.fake_ospf_lsas.append(
                     ssu.GlobalLie(dest, self.new_edge_metric, p))
     return self.fake_ospf_lsas
Пример #24
0
def add_dest_to_graph(dest, graph, edges_src=None, spt=None,
                      node_data_gen=None, **kw):
    """Add dest to the graph, possibly updating the shortest paths object

    :param dest: The destination node, will be set as a prefix
    :param graph: The graph to which dest must be added if not present
    :param edges_src: The source of edges to add in order to add dest,
                    if None, defaults to the sinks in the graph,
                    otherwise it is a function returning a list of edges
                    and taking dest as argument
    :param spt: The ShortestPath object to update to account for the new node
                if applicable
    :param node_data_gen: A function that will generate data for the new node
                         if needed
    :param kw: Extra parameters for the edges if any"""
    added = None
    if dest in graph and not _is_fake_dest(graph, dest):
        # Unless dest was only announced through fake links, we don't touch it
        log.debug('%s is already in the graph', dest)
        in_dag = True
    else:
        in_dag = False
        if not edges_src:
            added = []
            sinks = find_sink(graph)
            if not sinks:
                log.info('No sinks found in the graph!')
            for node in sinks:
                if node == dest:
                    continue
                log.info('Connected %s to %s in the graph', node, dest)
                _add_fake_route(graph, node, dest, **kw)
                added.append(node)
        else:
            added = edges_src(dest)
            log.info('Connecting edges sources %s to the graph to %s',
                     dest, added)
            for s in added:
                _add_fake_route(graph, s, dest, **kw)
            graph.add_edges_from((s, dest) for s in added, **kw)
    ndata = {} if not node_data_gen else node_data_gen()
    # Only update the dest node if explicitely requested
    if node_data_gen or not in_dag:
        graph.add_node(dest, prefix=True, **ndata)
    if added and spt:
        log.info('Updating SPT')
        _update_paths_towards(spt, graph, dest, added)
Пример #25
0
def add_dest_to_graph(dest, graph, edges_src=None, spt=None, node_data_gen=None, **kw):
    """Add dest to the graph, possibly updating the shortest paths object

    :param dest: The destination node, will be set as a prefix
    :param graph: The graph to which dest must be added if not present
    :param edges_src: The source of edges to add in order to add dest,
                    if None, defaults to the sinks in the graph,
                    otherwise it is a function returning a list of edges
                    and taking dest as argument
    :param spt: The ShortestPath object to update to account for the new node
                if applicable
    :param node_data_gen: A function that will generate data for the new node
                         if needed
    :param kw: Extra parameters for the edges if any"""
    added = None
    if dest in graph and not _is_fake_dest(graph, dest):
        # Unless dest was only announced through fake links, we don't touch it
        log.debug("%s is already in the graph", dest)
        in_dag = True
    else:
        in_dag = False
        if not edges_src:
            added = []
            sinks = find_sink(graph)
            if not sinks:
                log.info("No sinks found in the graph!")
            for node in sinks:
                if node == dest:
                    continue
                log.info("Connected %s to %s in the graph", node, dest)
                _add_fake_route(graph, node, dest, **kw)
                added.append(node)
        else:
            added = edges_src(dest)
            log.info("Connecting edges sources %s to the graph to %s", dest, added)
            for s in added:
                _add_fake_route(graph, s, dest, **kw)
            graph.add_edges_from((s, dest) for s in added, **kw)
    ndata = {} if not node_data_gen else node_data_gen()
    # Only update the dest node if explicitely requested
    if node_data_gen or not in_dag:
        graph.add_node(dest, prefix=True, **ndata)
    if added and spt:
        log.info("Updating SPT")
        _update_paths_towards(spt, graph, dest, added)
Пример #26
0
def add_dest_to_graph(dest, graph, edges_src=None, spt=None,
                      node_data_gen=None, **kw):
    """Add dest to the graph, possibly updating the shortest paths object

    :param dest: The destination node, will be set as a prefix
    :param graph: The graph to which dest must be added if not present
    :param edges_src: The source of edges to add in order to add dest,
                    if None, defaults to the sinks in the graph,
                    otherwise it is a function returning a list of edges
                    and taking dest as argument
    :param spt: The ShortestPath object to update to account for the new node
                if applicable
    :param node_data_gen: A function that will generate data for the new node
                         if needed
    :param kw: Extra parameters for the edges if any"""
    if dest in graph:
        log.debug('%s is already in the graph', dest)
        return

    if not edges_src:
        added = []
        sinks = find_sink(graph)
        if not sinks:
            log.info('No sinks found in the graph!')
        for node in sinks:
            log.info('Connected %s to %s in the graph', node, dest)
            # TODO cleanup, atm. some places use DiGraph other IGPGraph ...
            graph.add_edge(node, dest, **kw)
            added.append(node)
    else:
        added = edges_src(dest)
        log.info('Adding edges sources %s to the graph', added)
        graph.add_edges_from((s, dest) for s in added, **kw)
    ndata = {} if not node_data_gen else node_data_gen()
    graph.add_node(dest, prefix=True, **ndata)
    if added and spt:
        log.info('Updating SPT')
        _update_paths_towards(spt, graph, dest, added)
Пример #27
0
 def add_edge(self, source, destination, metric):
     log.info("Adding %s-%s @ %s", source, destination, metric)
     self.graph.add_edge(source, destination, cost=metric)
Пример #28
0
 def add_dest_to_graphs(self, dest, dag):
     if dest not in dag:
         for node in ssu.find_sink(dag):
             logger.info('Connected %s to %s in the DAG', node, dest)
             dag.add_edge(node, dest)
 def remove_edge(self, source, destination):
     log.info('Removing %s-%s', source, destination)
     self.graph.remove_edge(source, destination)
 def boostrap_graph(self, graph):
     log.info('Received graph: %s', graph)
     for u, v, m in graph:
         self.graph.add_edge(u, v, cost=m)
Пример #31
0
 def run(self):
     log.info('Connecting to server ...')
     self.json_proxy.communicate()
 def add_edge(self, source, destination, metric):
     log.info('Adding %s-%s @ %s', source, destination, metric)
     self.graph.add_edge(source, destination, cost=metric)
Пример #33
0
 def remove_edge(self, source, destination):
     log.info("Removing %s-%s", source, destination)
     self.graph.remove_edge(source, destination)
Пример #34
0
 def solve(self, graph, requirements):
     """Compute the augmented topology for a given graph and a set of
     requirements.
     :type graph: IGPGraph
     :type requirements: { dest: IGPGraph }
     :param requirements: the set of requirement DAG on a per dest. basis
     :return: list of fake LSAs"""
     self.reqs = requirements
     log.info('Preparing IGP graph')
     self.g = prepare_graph(graph, requirements)
     log.info('Computing SPT')
     self._p = ShortestPath(graph)
     lsa = []
     for dest, dag in requirements.iteritems():
         self.dest, self.dag = dest, dag
         self.ecmp.clear()
         log.info('Evaluating requirement %s', dest)
         log.info('Ensuring the consistency of the DAG')
         self.check_dest()
         ssu.complete_dag(self.dag, self.g, self.dest, self._p,
                          skip=self.reqs.keys())
         log.info('Computing original and required next-hop sets')
         for n, node in self.nodes():
             node.forced_nhs = set(self.dag.successors(n))
             node.original_nhs = set([p[1] for p in
                                      self._p.default_path(n, self.dest)])
         if not ssu.solvable(self.dag, self.g):
             log.warning('Consistency check failed, skipping %s', dest)
             continue
         log.info('Placing initial fake nodes')
         self.place_fake_nodes()
         log.info('Initializing fake nodes')
         self.initialize_fake_nodes()
         log.info('Propagating initial lower bounds')
         self.propagate_lb()
         log.debug('Fake node bounds: %s',
                   [n for _, n in self.nodes() if n.has_any_fake_node()])
         log.info('Reducing the augmented topology')
         self.merge_fake_nodes()
         self.remove_redundant_fake_nodes()
         log.info('Generating LSAs')
         lsas = self.create_fake_lsa()
         log.info('Solved the DAG for destination %s with LSA set: %s',
                  self.dest, lsas)
         lsa.extend(lsas)
     return lsa
Пример #35
0
 def boostrap_graph(self, graph):
     log.info("Received graph: %s", graph)
     for u, v, m in graph:
         self.graph.add_edge(u, v, cost=m)
Пример #36
0
 def print_net(self):
     """
     Log a representation of the fibbing network state
     """
     log.info('----------------------')
     log.info('Network of %s nodes', len(self.nodes))
     log.info('----------------------')
     log.info('')
     r_info = str(self.root)
     for l in r_info.split('\n'):
         log.info(l)
     log.info('|')
     bid_str = '%s --+' % self.bridge.id
     prefix = ' ' * (len(bid_str) - 1)
     log.info(bid_str)
     for n in self.nodes.values():
         if n == self.root:
             continue
         log.info('%s|', prefix)
         log.info('%s+--- %s', prefix, n)
 def run(self):
     """Connect the the southbound controller. This call will not return
     unless the connection is halted."""
     log.info('Connecting to server ...')
     self.json_proxy.communicate()
Пример #38
0
def check_fwd_dags(fwd_req, topo, lsas, solver):
    correct = True
    topo = topo.copy()
    # Check that the topology/dag contain the destinations, otherwise add it
    for dest, dag in fwd_req.iteritems():
        dest_in_dag = dest in dag
        dest_in_graph = dest in topo
        if not dest_in_dag or not dest_in_graph:
            if not dest_in_dag:
                sinks = ssu.find_sink(dag)
            else:
                sinks = dag.predecessors(dest)
            for s in sinks:
                if not dest_in_dag:
                    dag.add_edge(s, dest)
                if not dest_in_graph:
                    topo.add_edge(s, dest, metric=solver.new_edge_metric)
    fake_nodes = {}
    local_fake_nodes = collections.defaultdict(list)
    f_ids = set()
    for lsa in lsas:
        if lsa.cost > 0:
            f_id = '__f_%s_%s_%s' % (lsa.node, lsa.nh, lsa.dest)
            f_ids.add(f_id)
            fake_nodes[(lsa.node, f_id, lsa.dest)] = lsa.nh
            cost = topo[lsa.node][lsa.nh]['metric']
            topo.add_edge(lsa.node, f_id, metric=cost)
            topo.add_edge(f_id, lsa.dest, metric=lsa.cost - cost)
            log.debug(
                'Added a globally-visible fake node: '
                '%s - %s - %s - %s - %s [-> %s]', lsa.node, cost, f_id,
                lsa.cost - cost, lsa.dest, lsa.nh)
        else:
            local_fake_nodes[(lsa.node, lsa.dest)].append(lsa.nh)
            log.debug('Added a locally-visible fake node: %s -> %s', lsa.node,
                      lsa.nh)

    spt = ssu.all_shortest_paths(topo, metric='metric')
    for dest, req_dag in fwd_req.iteritems():
        log.info('Validating requirements for dest %s', dest)
        dag = IGPGraph()
        for n in filter(lambda n: n not in fwd_req, topo):
            if n in f_ids:
                continue
            log.debug('Checking paths of %s', n)
            for p in spt[n][0][dest]:
                log.debug('Reported path: %s', p)
                for u, v in zip(p[:-1], p[1:]):
                    try:  # Are we using a globally-visible fake node?
                        nh = fake_nodes[(u, v, dest)]
                        log.debug(
                            '%s uses the globally-visible fake node %s '
                            'to get to %s', u, v, nh)
                        dag.add_edge(u, nh)  # Replace by correct next-hop
                        break
                    except KeyError:
                        # Are we using a locally-visible one?
                        nh = local_fake_nodes[(u, dest)]
                        if nh:
                            log.debug(
                                '%s uses a locally-visible fake node'
                                ' to get to %s', u, nh)
                            for h in nh:
                                dag.add_edge(u, h)  # Replace by true nh
                            break
                        else:
                            dag.add_edge(u, v)  # Otherwise follow the SP
        # Now that we have the current fwing dag, compare to the requirements
        for n in req_dag:
            successors = set(dag.successors(n))
            req_succ = set(req_dag.successors(n))
            if successors ^ req_succ:
                log.error(
                    'The successor sets for node %s differ, '
                    'REQ: %s, CURRENT: %s', n, req_succ, successors)
                correct = False
            predecessors = set(dag.predecessors(n))
            req_pred = set(req_dag.predecessors(n))
            # Also requires to have a non-null successor sets to take into
            # account the fact that the destination will have new adjacencies
            # through fake nodes
            if predecessors ^ req_pred and successors:
                log.error(
                    'The predecessors sets for %s differ, '
                    'REQ: %s, CURRENT: %s', n, req_pred, predecessors)
                correct = False
    if correct:
        log.info('All forwarding requirements are enforced!')
    return correct
 def run(self):
     """Connect the the southbound controller. This call will not return
     unless the connection is halted."""
     log.info('Connecting to server ...')
     self.json_proxy.communicate()
Пример #40
0
 def print_net(self):
     """
     Log a representation of the fibbing network state
     """
     log.info('----------------------')
     log.info('Network of %s nodes', len(self.nodes))
     log.info('----------------------')
     log.info('')
     r_info = str(self.root)
     for l in r_info.split('\n'):
         log.info(l)
     log.info('|')
     bid_str = '%s --+' % self.bridge.id
     prefix = ' ' * (len(bid_str) - 1)
     log.info(bid_str)
     for n in self.nodes.values():
         if n == self.root:
             continue
         log.info('%s|', prefix)
         log.info('%s+--- %s', prefix, n)
Пример #41
0
    def apply_merge(self, n, s, lb, ub, nh):
        """Try to apply a given merge, n->s, with new lb/ub for s,
        and corresponding to the nexthop of n nh"""
        undos = []
        propagation_failure = []

        def undo_all():
            log.debug('Undoing all changes')
            for (f, args, kw) in reversed(undos):
                f(*args, **kw)

        def record_undo(f, *args, **kw):
            undos.append((f, args, kw))

        def propagation_fail(n):
            log.debug('The propagation failed on node %s, aborting merge!', n)
            propagation_failure.append(False)
            return True

        def propagation_assign(node, lb):
            record_undo(setattr, node, 'lb', node.lb)
            log.debug('Propagation caused the LB of %s to increase by %s',
                      node, lb)
            Node.increase_lb(node, lb)

        log.debug('Trying to apply merge, n: %s, s:%s, lb:%s, ub:%s, nh:%s', n,
                  s, lb, ub, nh)
        # Remove the fake node
        node = self.node(n)
        node.forced_nhs.remove(nh)
        record_undo(node.forced_nhs.add, nh)

        # Update the values in its successor
        succ_node = self.node(s)
        path_cost_increase = (self._p.default_cost(n, s) + succ_node.lb -
                              node.lb)
        record_undo(setattr, succ_node, 'lb', succ_node.lb)
        record_undo(setattr, succ_node, 'ub', succ_node.ub)
        succ_node.lb = lb
        succ_node.ub = ub

        ecmp_deps = list(self.ecmp_dep(n))
        log.debug('Checking merge effect on ECMP dependencies of %s: %s', n,
                  ecmp_deps)
        if s in ecmp_deps:
            log.debug(
                'Aborting merge has %s and %s are ECMP dependent: '
                'Merging them would make it impossible to keep both path'
                ' with the same cost!', n, s)
            undo_all()
            return
        remove_n = not node.has_fake_node(Node.GLOBAL)
        if remove_n:
            record_undo(node.add_fake_node, node.fake)
            node.remove_fake_node()
            log.debug(
                'Also removing %s from its ECMP deps has it no longer '
                'has a fake node.', n)
        deps = self.ecmp[s]
        for e in ecmp_deps:
            e_node = self.node(e)
            e_deps = self.ecmp[e]
            if remove_n:
                e_deps.remove(n)
                record_undo(e_deps.add, n)
                if e == n:
                    continue
            if e not in deps:
                deps.add(e)
                record_undo(deps.remove, e)
            if s not in e_deps:
                e_deps.add(s)
                record_undo(e_deps.remove, s)
            new_lb = e_node.lb + path_cost_increase
            if not self.valid_range(e, new_lb, e_node.ub):
                log.debug(
                    'Cannot increase the ECMP ecmp dep %s of %s by %s. '
                    'Aborting merge!', e, n, path_cost_increase)
                undo_all()
                return
            else:
                log.debug('Increased %s to %s', e, new_lb)
                record_undo(setattr, e_node, 'lb', e_node.lb)
                e_node.lb = new_lb

        ecmp_deps.append(s)
        log.debug('Propagating LB changes')
        self.propagate_lb(assign=propagation_assign,
                          fail_func=propagation_fail,
                          initial_nodes=ecmp_deps)
        if propagation_failure:
            undo_all()
        else:
            log.info('Merged %s into %s', n, s)
Пример #42
0
    def apply_merge(self, n, s, lb, ub, nh):
        """Try to apply a given merge, n->s, with new lb/ub for s,
        and corresponding to the nexthop of n nh"""
        undos = []
        propagation_failure = []

        def undo_all():
            log.debug('Undoing all changes')
            for (f, args, kw) in reversed(undos):
                f(*args, **kw)

        def record_undo(f, *args, **kw):
            undos.append((f, args, kw))

        def propagation_fail(n):
            log.debug('The propagation failed on node %s, aborting merge!', n)
            propagation_failure.append(False)
            return True

        def propagation_assign(node, lb):
            record_undo(setattr, node, 'lb', node.lb)
            log.debug('Propagation caused the LB of %s to increase by %s',
                      node, lb)
            Node.increase_lb(node, lb)

        log.debug('Trying to apply merge, n: %s, s:%s, lb:%s, ub:%s, nh:%s',
                  n, s, lb, ub, nh)
        # Remove the fake node
        node = self.node(n)
        node.forced_nhs.remove(nh)
        record_undo(node.forced_nhs.add, nh)

        # Update the values in its successor
        succ_node = self.node(s)
        path_cost_increase = (self._p.default_cost(n, s) +
                              succ_node.lb - node.lb)
        record_undo(setattr, succ_node, 'lb', succ_node.lb)
        record_undo(setattr, succ_node, 'ub', succ_node.ub)
        succ_node.lb = lb
        succ_node.ub = ub

        ecmp_deps = list(self.ecmp_dep(n))
        log.debug('Checking merge effect on ECMP dependencies of %s: %s',
                  n, ecmp_deps)
        if s in ecmp_deps:
            log.debug('Aborting merge has %s and %s are ECMP dependent: '
                      'Merging them would make it impossible to keep both path'
                      ' with the same cost!', n, s)
            undo_all()
            return
        remove_n = not node.has_fake_node(Node.GLOBAL)
        if remove_n:
            record_undo(node.add_fake_node, node.fake)
            node.remove_fake_node()
            log.debug('Also removing %s from its ECMP deps has it no longer '
                      'has a fake node.', n)
        deps = self.ecmp[s]
        for e in ecmp_deps:
            e_node = self.node(e)
            e_deps = self.ecmp[e]
            if remove_n:
                e_deps.remove(n)
                record_undo(e_deps.add, n)
                if e == n:
                    continue
            if e not in deps:
                deps.add(e)
                record_undo(deps.remove, e)
            if s not in e_deps:
                e_deps.add(s)
                record_undo(e_deps.remove, s)
            new_lb = e_node.lb + path_cost_increase
            if not self.valid_range(e, new_lb, e_node.ub):
                log.debug('Cannot increase the ECMP ecmp dep %s of %s by %s. '
                          'Aborting merge!', e, n, path_cost_increase)
                undo_all()
                return
            else:
                log.debug('Increased %s to %s', e, new_lb)
                record_undo(setattr, e_node, 'lb', e_node.lb)
                e_node.lb = new_lb

        ecmp_deps.append(s)
        log.debug('Propagating LB changes')
        self.propagate_lb(assign=propagation_assign,
                          fail_func=propagation_fail,
                          initial_nodes=ecmp_deps)
        if propagation_failure:
            undo_all()
        else:
            log.info('Merged %s into %s', n, s)
Пример #43
0
 def solve(self, graph, requirements):
     """Compute the augmented topology for a given graph and a set of
     requirements.
     :type graph: IGPGraph
     :type requirements: { dest: IGPGraph }
     :param requirements: the set of requirement DAG on a per dest. basis
     :return: list of fake LSAs"""
     self.reqs = requirements
     log.info('Preparing IGP graph')
     self.g = prepare_graph(graph, requirements)
     log.info('Computing SPT')
     self._p = ShortestPath(graph)
     lsa = []
     for dest, dag in requirements.iteritems():
         self.dest, self.dag = dest, dag
         self.ecmp.clear()
         log.info('Evaluating requirement %s', dest)
         log.info('Ensuring the consistency of the DAG')
         self.check_dest()
         ssu.complete_dag(self.dag,
                          self.g,
                          self.dest,
                          self._p,
                          skip=self.reqs.keys())
         log.info('Computing original and required next-hop sets')
         for n, node in self.nodes():
             node.forced_nhs = set(self.dag.successors(n))
             node.original_nhs = set(
                 [p[1] for p in self._p.default_path(n, self.dest)])
         if not ssu.solvable(self.dag, self.g):
             log.warning('Consistency check failed, skipping %s', dest)
             continue
         log.info('Placing initial fake nodes')
         self.place_fake_nodes()
         log.info('Initializing fake nodes')
         self.initialize_fake_nodes()
         log.info('Propagating initial lower bounds')
         self.propagate_lb()
         log.debug('Fake node bounds: %s',
                   [n for _, n in self.nodes() if n.has_any_fake_node()])
         log.info('Reducing the augmented topology')
         self.merge_fake_nodes()
         self.remove_redundant_fake_nodes()
         log.info('Generating LSAs')
         lsas = self.create_fake_lsa()
         log.info('Solved the DAG for destination %s with LSA set: %s',
                  self.dest, lsas)
         lsa.extend(lsas)
     return lsa
Пример #44
0
def check_fwd_dags(fwd_req, topo, lsas, solver):
    correct = True
    topo = topo.copy()
    # Check that the topology/dag contain the destinations, otherwise add it
    for dest, dag in fwd_req.iteritems():
        dest_in_dag = dest in dag
        dest_in_graph = dest in topo
        if not dest_in_dag or not dest_in_graph:
            if not dest_in_dag:
                sinks = ssu.find_sink(dag)
            else:
                sinks = dag.predecessors(dest)
            for s in sinks:
                if not dest_in_dag:
                    dag.add_edge(s, dest)
                if not dest_in_graph:
                    topo.add_edge(s, dest, metric=solver.new_edge_metric)
    fake_nodes = {}
    local_fake_nodes = collections.defaultdict(list)
    f_ids = set()
    for lsa in lsas:
        if lsa.cost > 0:
            if not lsa.node:  # We added a pure fake LSA
                continue
            f_id = '__f_%s_%s_%s' % (lsa.node, lsa.nh, lsa.dest)
            f_ids.add(f_id)
            fake_nodes[(lsa.node, f_id, lsa.dest)] = lsa.nh
            cost = topo[lsa.node][lsa.nh]['metric']
            topo.add_edge(lsa.node, f_id, metric=cost)
            topo.add_edge(f_id, lsa.dest, metric=lsa.cost - cost)
            log.debug('Added a globally-visible fake node: '
                      '%s - %s - %s - %s - %s [-> %s]',
                      lsa.node, cost, f_id, lsa.cost - cost, lsa.dest, lsa.nh)
        else:
            local_fake_nodes[(lsa.node, lsa.dest)].append(lsa.nh)
            log.debug('Added a locally-visible fake node: %s -> %s',
                      lsa.node, lsa.nh)

    spt = ssu.all_shortest_paths(topo, metric='metric')
    for dest, req_dag in fwd_req.iteritems():
        log.info('Validating requirements for dest %s', dest)
        dag = IGPGraph()
        for n in filter(lambda n: n not in fwd_req, topo):
            if n in f_ids:
                continue
            log.debug('Checking paths of %s', n)
            for p in spt[n][0][dest]:
                log.debug('Reported path: %s', p)
                for u, v in zip(p[:-1], p[1:]):
                    try:  # Are we using a globally-visible fake node?
                        nh = fake_nodes[(u, v, dest)]
                        log.debug('%s uses the globally-visible fake node %s '
                                  'to get to %s', u, v, nh)
                        dag.add_edge(u, nh)  # Replace by correct next-hop
                        break
                    except KeyError:
                            # Are we using a locally-visible one?
                            nh = local_fake_nodes[(u, dest)]
                            if nh:
                                log.debug('%s uses a locally-visible fake node'
                                          ' to get to %s', u, nh)
                                for h in nh:
                                    dag.add_edge(u, h)  # Replace by true nh
                                break
                            else:
                                dag.add_edge(u, v)  # Otherwise follow the SP
        # Now that we have the current fwing dag, compare to the requirements
        for n in req_dag:
            successors = set(dag.successors(n))
            req_succ = set(req_dag.successors(n))
            if successors ^ req_succ:
                log.error('The successor sets for node %s differ, '
                          'REQ: %s, CURRENT: %s', n, req_succ, successors)
                correct = False
            predecessors = set(dag.predecessors(n))
            req_pred = set(req_dag.predecessors(n))
            # Also requires to have a non-null successor sets to take into
            # account the fact that the destination will have new adjacencies
            # through fake nodes
            if predecessors ^ req_pred and successors:
                log.error('The predecessors sets for %s differ, '
                          'REQ: %s, CURRENT: %s', n, req_pred, predecessors)
                correct = False
    if correct:
        log.info('All forwarding requirements are enforced!')
    return correct
Пример #45
0
 def _refresh_augmented_topo(self):
     log.info('Solving topologies')
     self.optimizer.solve(self.igp_graph,
                          self.fwd_dags)