def insert(self, hash_code, succ_node): # create a client for this operation client = Simple_Client(self.host, self.port) # the insert request is like lookup msg = "INSERT|{0}|{1}|{2}".format(hash_code, self.host, self.port) # the server at succ is contacted, response is the pred node of our # node response = client.attempt_to_connect(succ_node.host, succ_node.port, msg) # the server returned the predecessor if response == "": print("NO INSERT RESPONSE ERROR!") exit(0) # the response should be a string of strings, separated by ':', but # there is no type checking, sorry # the response contained the pred node after the coord in the network # was complete pred_hash, pred_host, pred_port_str = response.split(':') # pred_port_str was sent as a string and now is converted to an int pred_port = int(pred_port_str) # we create a remote node for this pred pred_node = NetNode(pred_host, (int(pred_port))) # then we stop and return the remote node return pred_node
def __init__(self, host, port, peer_sock, local_sock, start_port, port_range): self.node = NetNode(host, port) self.router = Router(self.node) self.data = Data() self.peer_server = Simple_Server(peer_sock, self) self.local_listener = Simple_Listener(local_sock, self) self.start_port = start_port self.port_range = port_range
def lookup(self, hash_code, net_node): # create a client for this operation client = Simple_Client(self.host, self.port) # the lookup request is always the same msg = "LOOKUP|{0}|{1}|{2}".format(hash_code, self.host, self.port) succ_node = NetNode("None", 0) # since this implementation of lookup is iterative, so we need to keep # track of the nodes we've queried the last hash inits as the code we # are looking for, but is updated as the last node we checked last_hash = hash_code # our successor's hash inits as the entry node of the lookup succ_hash = net_node.hash succ_host = net_node.host succ_port = net_node.port # if the pred and succ hashes ever are equal, it means that the node we # are going to check next is the same node as the one we just checked, # which means we are done searching while not (last_hash == succ_hash): # the server at succ is contacted, response is the succ node to the # hash response = client.attempt_to_connect(succ_host, succ_port, msg) # print ("Lookup:" + response) # since we just checked succ, we can copy that info to last_hash last_hash = succ_hash # the response should be a few strings, separated by ':', but there # is no type checking, sorry if not response == "": # the response contained the next node to check, so it is # assigned to the current successor succ_hash, succ_host, succ_port_str = response.split(':') # succ_port_str was sent as a string and now is converted to an # int succ_port = int(succ_port_str) else: return ("NO LOOKUP RESPONSE ERROR!") # we create a remote node for this succ succ_node = NetNode(succ_host, (int(succ_port))) # the server returned self or its succ, if it returned self, then we # will exit the loop and return return succ_node
def find_peer(self, target_host, min_port, port_range): # the concept is to start at a random port, loop around until we get to a host that will respond max_port = min_port + port_range # find the max range random_offset = 0 # = random.randint(min_port, self.port) # choose a random port in range # create a client for this operation client = Simple_Client(self.host, self.port) # instruct the client to 'broadcast' and loop through all peer addresses response = client.iterative_broadcast(target_host, min_port, random_offset, port_range, "FIND_PEER") if response == "": return (NetNode("None", 0)) # the response should be a tuple, separated by ':', but there is no type checking, sorry r_host, r_port = response.split(':') # we create a remote node for this r_node = NetNode(r_host, (int(r_port))) # then we stop and return the remote node return r_node
def performAction(self, msg): m = msg.split('|') if (m[0] == "FIND_PEER"): #return this nodes info, easily done return self.host + ":" + str(self.port) if (m[0] == "LOOKUP"): #lookup() code = m[1] #hash_code being sent as a lookup host = m[2] #host of the requester port = int(m[3]) #port of the requester mine = self.head.node.hash #my hashed ID succ = self.head.router.succ.hash # my succ's hashed ID pred = self.head.router.pred.hash # my pred's hashed ID # 4 ways that the lookup returns my node (SELF RESPONSES INDICATE Completion) # 1: if pred = mine = succ: (one node network) if (pred == mine) and (mine == succ): #send succ(hash_code) = mine ID back to client return (mine + ":" + self.host + ":" + str(self.port)) # 2: if pred < hash_code and hash_code <= self and pred < self: # (no boundary check, if my node is the last, but not the first) if (pred < code) and (code <= mine) and (pred < mine): #send succ(hash_code) = mine ID back to client return (mine + ":" + self.host + ":" + str(self.port)) # 3: if pred < hash_code and hash_code > self and pred > self: # (pred is last node before flip, id first after, hash is before the flip) if (pred < code) and (code > mine) and (pred > mine): #send succ(hash_code) = mine ID back to client return (mine + ":" + self.host + ":" + str(self.port)) # 4: if pred > hash_code and hash_code <= self and pred > self: # (pred is last node before flip, id first after, hash is after the flip) if (pred > code) and (code <= mine) and (pred > mine): #send succ(hash_code) = mine ID back to client return (mine + ":" + self.host + ":" + str(self.port)) # ALL OTHER CASES Return the successor (which implicitly indicates non completion) # Alternate implementations could have server call lookup on successor (recursive) # But this implementation returns the succ for the client to do next lookup (iterative) return (succ + ":" + self.head.router.succ.host + ":" + str(self.head.router.succ.port)) if (m[0] == "INSERT"): #insert() code = m[1] #hash_code being sent in insert host = m[2] #host of the requester port = int(m[3]) #port of the requester # we need to save the old pred info in case we overwrite the object old_pred_code = self.head.router.pred.hash old_pred_host = self.head.router.pred.host old_pred_port = self.head.router.pred.port #creating the new predecessor object from the message new_pred = NetNode(host, port) #if this is a new network, set all to the new node, return self info if ((old_pred_code == self.head.node.hash) and (self.head.router.succ.hash == self.head.node.hash)): self.head.router.setPred(new_pred) self.head.router.setSucc(new_pred) self.head.router.setEntry(new_pred) return (old_pred_code + ":" + old_pred_host + ":" + str(old_pred_port)) #INSERT is the 1st of 3 messages, the 2nd is sent to the pred # we need a temp client for that temp_client = Simple_Client(self.host, self.port) # craft a message for the old_pred server to update its successor to the new pred msg = "UPDATE_SUCC" + "|" + code + "|" + host + "|" + str(port) # connect to the current predecessor to update pointer to succ response = temp_client.attempt_to_connect(old_pred_host, old_pred_port, msg) # if the response was "UPDATED_SUCC_OK" then we set the new_pred and return the old one if (response == "UPDATED_SUCC_OK"): # update the new predecessor self.head.router.setPred(new_pred) # return info on the old predecessor return (old_pred_code + ":" + old_pred_host + ":" + str(old_pred_port)) #else: return "" if (m[0] == "UPDATE_SUCC"): #update_succ() code = m[ 1] #hash_code being sent in update_succ is that of the new succ host = m[2] #host of the new succ port = int(m[3]) #port of the new succ #creating the new successor object from the message new_succ = NetNode(host, port) #updating the app_support node self.head.router.setSucc(new_succ) #no error checking yet return "UPDATED_SUCC_OK" if (m[0] == "WRITE_DATA"): #write() key = m[1] #hash_code being sent in write value = m[2] host = m[3] #host of the requester port = int(m[4]) #port of the requester self.head.data.write(key, value) return "OK" #Implement these handlers if (m[0] == "READ_DATA"): key = m[1] return self.head.data.read(key) #not using this, just write("0") if (m[0] == "DELETE_DATA"): return self.host + str(self.port) + " Reading Data..." return "CMD NOT FOUND"