def random_routing_table(n): table = RoutingTable() nodes = [i for i in range(n)] rd_prot = lambda: rd.choice(protocols) functions = [ AdaptationFunction(rd_prot(), rd_prot(), CV) for i in range(n) ] for i in range(3 * n): table.add_route(rd.choice(nodes), random_stack(protocols, 1, 10), rd.choice(nodes), rd.choice(functions), rd.randrange(100)) return table
def test_routing_table_add_valid(): table = RoutingTable() nodes = [i for i in range(10)] for node in nodes: H = random_stack(protocols, 20, 20) function = AdaptationFunction(H[-1], H[-1], CV) assert table.add_route(node, H, node, function, 1) row = table.get(node, H) assert row.cost == 1 and row.next_hop == node and row.function == function
def test_routing_table_replace_route(): table = RoutingTable() function = AdaptationFunction("a", "a", CV) assert table.add_route(0, ["a"], 1, function, 100) for i in range(1, 101): assert table.add_route(0, ["a"], 1, function, 100 - i) # == True
def test_routing_table_add_invalid(): table = RoutingTable() function = AdaptationFunction("a", "a", CV) assert table.add_route(0, ["a"], 1, function, 12) for i in range(30): assert not table.add_route(0, ["a"], 1, function, rd.randint(12, 20))
class Node(Thread): """ Each node represents a router in the network """ last_received = 0 last_conf_received = 0 def __init__(self, node_id, network, adapt_functions=None, **kwargs): """ :param node_id: any unique identifier :param network: a data structure holding every node """ Thread.__init__(self, **kwargs) # temp if adapt_functions is None: adapt_functions = [ AdaptationFunction(random.choice(("a", "b", "c")), random.choice(("a", "b", "c")), CV) for i in range(2) ] # --------------- communication related initialisation --------------- # # self.daemon = True self.id = node_id self.network = network self.wake_buffer = Queue() self.adapt_functions = adapt_functions self.routing_table = RoutingTable() for function in self.adapt_functions: self.routing_table.add_route(self.id, list(function._from), self.id, function, 0) # -------------------- counting sent and received -------------------- # self.conf_received = 0 self.conf_sent = 0 @property def neighbors_id(self): "returns the id of this node's neighbors" return list(self.network.graph.neighbors(self.id)) # ------------------------- adaptation functions ------------------------- # @property def In(self): return list(set([x._from for x in self.adapt_functions])) @property def Out(self): return list(set([x._to for x in self.adapt_functions])) # ---------------------- send and receive messages ----------------------- # def send(self, receiver_id, message): "sends a specific message to a node" if isinstance(message, ConfigurationMessage): self.conf_sent += 1 self.network.links[self.id, receiver_id].put(message) self.network.threads[receiver_id].wake_buffer.put(self.id) def receive(self, sender_id, message): "called upon reception of a message sent by another node" # ----------------------------- routing ------------------------------ # if isinstance(message, Message): if message.dest == self.id: self.destination_reached(message) else: self.route(sender_id, message) # ------------------ configuring the routing table ------------------- # if isinstance(message, ConfigurationMessage): self.conf_received += 1 for function in self.adapt_functions: msg_copy = message.copy() if function.reverse.appliable(msg_copy): in_stack = function.reverse.apply_to_stack(msg_copy.stack) if len(in_stack) <= self.network.max_stack: cost = self.network.links[self.id, sender_id].cost( function) + msg_copy.cost added = self.routing_table.add_route( msg_copy.dest, in_stack, sender_id, function, cost) if added: for n_id in self.neighbors_id: self.send( n_id, ConfigurationMessage( msg_copy.dest, in_stack, cost)) Node.last_conf_received = time() Node.last_received = time() # ---------------------------- route messages ---------------------------- # def route(self, sender_id, message): "uses the routing table to route any received message" stack, dest = message.stack, message.dest if (dest, stack) in self.routing_table: row = self.routing_table.get(dest, stack) row.function.apply(message) if message.valid: self.send(row.next_hop, message) else: print("message {} not valid, I do not have this entry".format( message)) def destination_reached(self, message): """Any message passed through this function was destined to this router""" print("The message reached its destination:\n\t- {}\n" \ "\tArrived to {}".format(message, self.id)) # ---------------------------- initialisation ---------------------------- # def init(self): """ Sends messages to each neighbors to initialise the routing table """ for x in self.In: for n_id in self.neighbors_id: self.send(n_id, ConfigurationMessage(self.id, list(x), 0)) # ------------------------ wait for notifications ------------------------ # def wait_for_messages(self): # print(">> {} Finished sending".format(self.id)) while self.network.running: try: sender = self.wake_buffer.get(timeout=1e-2) # blocks link = self.network.links[sender, self.id] while not link.empty(): item = link.get_nowait() self.receive(sender, item) self.wake_buffer.task_done() except Empty: pass # ---------------------------- run the thread ---------------------------- # def run(self): # self.condition.acquire() self.init() self.wait_for_messages()