def __init__(self, instance_number): """ :param instance_number: the controller instance number :param net: the subnet allocated for the fibbing nodes """ self.leader = False self.instance = instance_number self.name = 'c%s' % instance_number self.nodes = {} self.bridge = Bridge('br0', self.name) self.root = None net = ip_network(CFG.get(DEFAULTSECT, 'base_net')) controller_prefix = CFG.getint(DEFAULTSECT, 'controller_prefixlen') host_prefix = net.max_prefixlen - controller_prefix controller_base = (int(net.network_address) + (instance_number << host_prefix)) controller_net = ip_address(controller_base) self.net = ip_network('%s/%s' % (controller_net, controller_prefix)) self.graph_thread = daemon_thread(target=self.infer_graph, name="Graph inference thread") self.json_proxy = SJMPServer(hostname=CFG.get(DEFAULTSECT, 'json_hostname'), port=CFG.getint(DEFAULTSECT, 'json_port'), invoke=self.proxy_connected, target=FakeNodeProxyImplem(self)) self.json_thread = daemon_thread(target=self.json_proxy.communicate, name="JSON proxy thread") # Used to assign unique router-id to each node self.next_id = 1 self.links = [] # The fibbing routes self.routes = {} self.route_mappings = {}
def __init__(self, instance_number): """ :param instance_number: the controller instance number :param net: the subnet allocated for the fibbing nodes """ self.leader = False self.instance = instance_number self.name = 'c%s' % instance_number self.nodes = {} self.bridge = Bridge('br0', self.name) self.root = None net = ip_network(CFG.get(DEFAULTSECT, 'base_net')) controller_prefix = CFG.getint(DEFAULTSECT, 'controller_prefixlen') host_prefix = net.max_prefixlen - controller_prefix controller_base = (int(net.network_address) + (instance_number << host_prefix)) controller_net = ip_address(controller_base) self.net = ip_network('%s/%s' % (controller_net, controller_prefix)) self.graph_thread = Thread(target=self.infer_graph, name="Graph inference thread") self.json_proxy = SJMPServer(hostname=CFG.get(DEFAULTSECT, 'json_hostname'), port=CFG.getint(DEFAULTSECT, 'json_port'), invoke=self.proxy_connected, target=FakeNodeProxyImplem(self)) self.json_thread = Thread(target=self.json_proxy.communicate) # Used to assign unique router-id to each node self.next_id = 1 self.links = [] # The fibbing routes self.routes = {} self.route_mappings = {}
class FibbingManager(object): def __init__(self, instance_number): """ :param instance_number: the controller instance number :param net: the subnet allocated for the fibbing nodes """ self.leader = False self.instance = instance_number self.name = 'c%s' % instance_number self.nodes = {} self.bridge = Bridge('br0', self.name) self.root = None net = ip_network(CFG.get(DEFAULTSECT, 'base_net')) controller_prefix = CFG.getint(DEFAULTSECT, 'controller_prefixlen') host_prefix = net.max_prefixlen - controller_prefix controller_base = (int(net.network_address) + (instance_number << host_prefix)) controller_net = ip_address(controller_base) self.net = ip_network('%s/%s' % (controller_net, controller_prefix)) self.graph_thread = Thread(target=self.infer_graph, name="Graph inference thread") self.json_proxy = SJMPServer(hostname=CFG.get(DEFAULTSECT, 'json_hostname'), port=CFG.getint(DEFAULTSECT, 'json_port'), invoke=self.proxy_connected, target=FakeNodeProxyImplem(self)) self.json_thread = Thread(target=self.json_proxy.communicate) # Used to assign unique router-id to each node self.next_id = 1 self.links = [] # The fibbing routes self.routes = {} self.route_mappings = {} def start(self, phys_ports, nodecount=None): """ Start the fibbing network :param nodecount: Pre-allocate nodecount fibbing nodes """ # Create root node self.root = self.add_node(id='root', cls=RootRouter, start=False) del self.nodes[self.root.id] # The root node should not originate LSA self.graph_thread.start() self.json_thread.start() # And map all physical ports to it ports = gen_physical_ports(phys_ports) for name, addr in ports: link = PhysicalLink(self.root, name, addr) self.root.add_physical_link(link) self.root.start() # Create additional nodes if requested while nodecount > 0: self.add_node() nodecount -= 1 def add_node(self, id=None, cls=Router, start=True): """ Add a new fibbing node to the network and start it :param id: The name of the new node :param cls: The class to use to instantiate it :param start: Automatically start the node :return: The new node instance """ # Create a node n = self.create_node(id, cls) # Link it to the bridge l = self.link(self.bridge, n) # Generate unique ip for its interface that's connected to the bridge router_ip = ip_interface('%s/%s' % (self.net[self.next_id], self.net.prefixlen)) self.next_id += 1 l.dst.set_ip(router_ip) if start: n.start() return n 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 __getitem__(self, item): try: return self.nodes[item] except KeyError as e: if item == 'root': return self.root else: raise e def link(self, src, dst): """ Create a veth Link between two nodes :param src: The source node of the link :param dst: The destination node of the link """ log.debug('Linking %s to %s', src.id, dst.id) l = Link(src, dst) # Register the new link self.links.append(l) return l 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 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) def cleanup(self): """ Cleanup all namespaces/links/... """ self.json_proxy.stop() for link in self.links: link.delete() self.root.delete() for node in self.nodes.values(): node.delete() self.bridge.delete() def install_route(self, network, points, advertize): """ Install and advertize a fibbing route :param network: the network prefix to attract :param points: a list of (address, metric) of points """ net = ip_network(network) # Retrieve existing route if any try: route = self.routes[net] except KeyError: route = FibbingRoute(net, []) self.routes[net] = route self.route_mappings[net] = set() # Get used nodes mapping for this prefix mappings = self.route_mappings[net] # Increase node count if needed size = len(route) + len(points) while size > len(self.nodes): self.add_node() # Get available node list for this prefix nodes = [n for n in self.nodes.values() if n not in mappings] # Generate attraction points attraction_points = [AttractionPoint(addr, metric, nodes.pop()) for addr, metric in points if addr] # Update used nodes mapping for p in attraction_points: mappings.add(p.node) # Advertize them route.append(attraction_points, advertize) def remove_route(self, network): """ Remove a route :param network: The prefix to remove """ net = ip_network(network) try: route = self.routes[net] self.remove_route_part(net, *[p for p in route]) except KeyError: log.debug('No route for network %s', net) def remove_route_part(self, network, advertize, *elems): """ Remove elements of a route :param network: The prefix of the route :param elems: The list of forwarding address to remove """ log.debug('Removing route for prefix %s, elements: %s', network, elems) net = ip_network(network) try: route = self.routes[net] for e in elems: node = route.retract(e, advertize) if node: self.route_mappings[net].remove(node) if len(route) == 0: self.route_mappings.pop(net) self.routes.pop(net) except KeyError: log.debug('No route for network %s', network) def infer_graph(self): self.root.parse_lsdblog() def _get_proxy_routes(self, points): for prefix, parts in groupby(sorted(points, key=itemgetter(3)), key=itemgetter(3)): route = [] for p in parts: src, dst, cost = p[0], p[1], p[2] if cost == -1: cost = 1 else: src = None fwd_addr = self.root.get_fwd_address(src, dst) route.append((fwd_addr, str(cost))) yield prefix, route def proxy_add(self, points): """ :param points: (source, fwd, cost, prefix)* """ self.leader = self.instance == self.lsdb.get_leader() log.info('Shapeshifter added attraction points: %s', points) for prefix, route in self._get_proxy_routes(points): self.install_route(prefix, route, self.leader) def proxy_remove(self, points): """ :param points: (source, fwd, cost, prefix)* """ self.leader = self.instance == self.lsdb.get_leader() 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)) def proxy_connected(self, session): self.root.send_lsdblog_to(session) @property def lsdb(self): return self.root.lsdb
self.client.execute('sum', a, b=b) def do_exit(self, line): return True def do_info(self, line): # Query the remopte end for the supported methods/docs/args self.client.ask_info() def default(self, line): items = line.split(' ') self.client.execute(items[0], *items[1:]) if __name__ == '__main__': log.setLevel(logging.DEBUG) s = SJMPServer(H, P, target=EchoProxy()) c = SJMPClient(H, P) a = ProxyCloner(EchoProxy, c) log.debug(dir(a)) st = Thread(target=s.communicate, name='server') st.daemon = True st.start() log.debug('Started server') ct = Thread(target=c.communicate, name='client') ct.daemon = True ct.start() log.debug('Started client') a.echo('hello world') a.sum(1, 2) TestCLI(c).cmdloop() c.stop()
class FibbingManager(object): def __init__(self, instance_number): """ :param instance_number: the controller instance number :param net: the subnet allocated for the fibbing nodes """ self.leader = False self.instance = instance_number self.name = 'c%s' % instance_number self.nodes = {} self.bridge = Bridge('br0', self.name) self.root = None net = ip_network(CFG.get(DEFAULTSECT, 'base_net')) controller_prefix = CFG.getint(DEFAULTSECT, 'controller_prefixlen') host_prefix = net.max_prefixlen - controller_prefix controller_base = (int(net.network_address) + (instance_number << host_prefix)) controller_net = ip_address(controller_base) self.net = ip_network('%s/%s' % (controller_net, controller_prefix)) self.graph_thread = daemon_thread(target=self.infer_graph, name="Graph inference thread") self.json_proxy = SJMPServer(hostname=CFG.get(DEFAULTSECT, 'json_hostname'), port=CFG.getint(DEFAULTSECT, 'json_port'), invoke=self.proxy_connected, target=FakeNodeProxyImplem(self)) self.json_thread = daemon_thread(target=self.json_proxy.communicate, name="JSON proxy thread") # Used to assign unique router-id to each node self.next_id = 1 self.links = [] # The fibbing routes self.routes = {} self.route_mappings = {} def start(self, phys_ports, nodecount=None): """ Start the fibbing network :param nodecount: Pre-allocate nodecount fibbing nodes """ # Create root node self.root = self.add_node(id='root', cls=RootRouter, start=False) self.root.lsdb.set_leader_watchdog(self) del self.nodes[self.root.id] # The root node should not originate LSA self.graph_thread.start() self.json_thread.start() # And map all physical ports to it ports = gen_physical_ports(phys_ports) for name, addr in ports: link = PhysicalLink(self.root, name, addr) self.root.add_physical_link(link) self.root.start() # Create additional nodes if requested if nodecount is None: nodecount = CFG.getint(DEFAULTSECT, 'initial_node_count') while nodecount > 0: self.add_node() nodecount -= 1 def add_node(self, id=None, cls=Router, start=True): """ Add a new fibbing node to the network and start it :param id: The name of the new node :param cls: The class to use to instantiate it :param start: Automatically start the node :return: The new node instance """ # Create a node n = self.create_node(id, cls) # Link it to the bridge l = self.link(self.bridge, n) # Generate unique ip for its interface that's connected to the bridge router_ip = ip_interface('%s/%s' % (self.net[self.next_id], self.net.prefixlen)) self.next_id += 1 l.dst.set_ip(router_ip) if start: n.start() return n 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 __getitem__(self, item): try: return self.nodes[item] except KeyError as e: if item == 'root': return self.root else: raise e def link(self, src, dst): """ Create a veth Link between two nodes :param src: The source node of the link :param dst: The destination node of the link """ log.debug('Linking %s to %s', src.id, dst.id) l = Link(src, dst) # Register the new link self.links.append(l) return l 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 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) def cleanup(self): """ Cleanup all namespaces/links/... """ self.json_proxy.stop() for link in self.links: link.delete() self.root.delete() for node in self.nodes.values(): node.delete() self.bridge.delete() def install_route(self, network, points, advertize): """ Install and advertize a fibbing route :param network: the network prefix to attract :param points: a list of (address, metric) of points """ try: net = ip_network(network) except ValueError: log.error('%s is not a valid IP network', network) return try: points_data = [(ip_address(addr), int(metric)) for addr, metric in points] except ValueError as e: log.error("Failed to parse an attraction point: %s", e) return # Retrieve existing route if any try: route = self.routes[net] except KeyError: route = FibbingRoute(net, []) self.routes[net] = route self.route_mappings[net] = set() # Get used nodes mapping for this prefix mappings = self.route_mappings[net] # Increase node count if needed size = len(route) + len(points) while size > len(self.nodes): self.add_node() # Get available node list for this prefix nodes = [n for n in self.nodes.values() if n not in mappings] # Generate attraction points attraction_points = [AttractionPoint(addr, metric, nodes.pop()) for addr, metric in points_data] # Update used nodes mapping for p in attraction_points: mappings.add(p.node) # Advertize them route.append(attraction_points, advertize) def remove_route(self, network): """ Remove a route :param network: The prefix to remove """ net = ip_network(network) try: route = self.routes[net] self.remove_route_part(net, *[p for p in route]) except KeyError: log.debug('No route for network %s', net) except Exception as other_exception: log.exception(other_exception) def remove_route_part(self, network, advertize, *elems): """ Remove elements of a route :param network: The prefix of the route :param elems: The list of forwarding addresses to remove """ log.debug('Removing route for prefix %s, elements: %s', network, elems) net = ip_network(network) try: route = self.routes[net] for e in elems: node = route.retract(e, advertize) if node: self.route_mappings[net].remove(node) if len(route) == 0: self.route_mappings.pop(net) self.routes.pop(net) except KeyError: log.debug('No route for network %s', network) 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()) def infer_graph(self): self.root.parse_lsdblog() def _get_proxy_routes(self, points): for prefix, parts in groupby(sorted(points, key=itemgetter(3)), key=itemgetter(3)): route = [] for p in parts: src, dst, cost = p[0], p[1], p[2] if cost >= 0: src = None fwd_addr = self.root.get_fwd_address(src, dst) # Can have multiple private addresses per interface, handle # here the selection ... if isinstance(fwd_addr, list): try: fwd_addr = fwd_addr[cost] except IndexError: log.warning('Required private forwarding address index' ' is out of bounds. Wanted index %s ' '- Have %s elements.', abs(cost), len(fwd_addr)) fwd_addr = fwd_addr[0] cost = 1 try: fwd_addr = str(ip_interface(fwd_addr).ip) except ValueError: log.debug('Forwarding address for %s-%s has no netmask: %s', src, dst, fwd_addr) route.append((fwd_addr, str(cost))) yield prefix, route 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) 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)) def proxy_connected(self, session): self.root.send_lsdblog_to(session) self.remove_session_routes(session) @property def lsdb(self): return self.root.lsdb 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')