from dht_common import between, SIZE_OF_HASH, SIZE_OF_IP_ADDRESS class Base_Node(object): def __init__(self, nodeID, documentSize, numItems=0, table=[]): self.nodeID = nodeID self.nextNode = None self.documentSize = documentSize self.numItems = numItems self.table = dict(table) self.numRounds = 0 self.numMessagesSent = 0 self.numMessagesRecv = 0 self.numBytesSent = 0 self.numBytesRecv = 0 self.lastNumRounds = 0 self.lastNumMessagesSent = 0 self.lastNumMessagesRecv = 0 self.lastNumBytesSent = 0 self.lastNumBytesRecv = 0 def insert(self): self.numItems += 1 self.numRounds += 1 self.numMessagesSent += 1 self.numMessagesRecv += 1 self.numBytesSent += SIZE_OF_HASH self.numBytesRecv += self.documentSize # There has to be some way of inserting finger table values, # so this is it (Normally only the DHT should be interfacing with it) def insert_relation(self, nodeID, fingerTableValue): # nextNode is kept up to date so that when doing finger table searches, # we can know when we own something or it needs to go somewhere else if not self.nextNode or between(nodeID, self.nodeID, self.nextNode): self.nextNode = nodeID self.table[nodeID] = fingerTableValue def retrieve(self): self.numRounds += 1 self.numMessagesSent += 1 self.numMessagesRecv += 1 self.numBytesSent += self.documentSize self.numBytesRecv += SIZE_OF_HASH def get_finger_table_val(self, searchID): # if we should own this ID, say so if between(searchID, self.nodeID, self.nextNode): retval = (self.nodeID, -1) else: # otherwise, find the best option in the finger table and give that back options = [x for (x,y) in self.table.items() if x <= searchID] closest = max(options) if len(options) > 0 else max([x for (x,y) in self.table.items()]) retval = (closest, self.table[closest]) self.numRounds += 1 self.numMessagesSent += 1 self.numMessagesRecv += 1 self.numBytesSent += SIZE_OF_HASH + SIZE_OF_IP_ADDRESS self.numBytesRecv += SIZE_OF_HASH return retval def get_num_rounds(self): self.lastNumRounds = self.numRounds return self.numRounds def get_recent_num_rounds(self): retval = self.numRounds - self.lastNumRounds self.lastNumRounds = self.numRounds return retval def get_num_messages_sent(self): self.lastNumMessagesSent = self.numMessagesSent return self.numMessagesSent def get_recent_num_messages_sent(self): retval = self.numMessagesSent - self.lastNumMessagesSent self.lastNumMessagesSent = self.numMessagesSent return retval def get_num_messages_recv(self): self.lastNumMessagesRecv = self.numMessagesRecv return self.numMessagesRecv def get_recent_num_messages_recv(self): retval = self.numMessagesRecv - self.lastNumMessagesRecv self.lastNumMessagesRecv = self.numMessagesRecv return retval def get_num_bytes_sent(self): self.lastNumBytesSent = self.numBytesSent return self.numBytesSent def get_recent_num_bytes_sent(self): retval = self.numBytesSent - self.lastNumBytesSent self.lastNumBytesSent = self.numBytesSent return retval def get_num_bytes_recv(self): self.lastNumBytesRecv = self.numBytesRecv return self.numBytesRecv def get_recent_num_bytes_recv(self): retval = self.numBytesRecv - self.lastNumBytesRecv self.lastNumBytesRecv = self.numBytesRecv return retval # Normally this file is a class to be used elsewhere, # but if you run it directly it performs some rudimentary unit tests # TODO: Add unit tests for size calculations if __name__ == "__main__": SIZE_OF_DOCUMENTS_IN_TEST = 1024 test = Base_Node(0, SIZE_OF_DOCUMENTS_IN_TEST) [test.insert(x, x) for x in range(10)] retrievals = [test.retrieve(x) for x in range(10)] print("Insert and retrieval fires correctly.") test.retrieve("nothere") print("Nonexistant entries fires correctly.") test.insert_relation(1, 1) test.insert_relation(2, 2) test.insert_relation(4, 4) test.insert_relation(8, 8) assert test.get_finger_table_val(1) == (1, 1) assert test.get_finger_table_val(3) == (2, 2) assert test.get_finger_table_val(4) == (4, 4) assert test.get_finger_table_val(7) == (4, 4) assert test.get_finger_table_val(0) == (0, -1) print("Finger table stuff working correctly")