def __init__(self, ipadr, serverPort, first): """ Constructor ipadr: our ip address serverPort: port to bind to first: indicator if we are the first node 1=true """ self.i = 1 self.dMessages = dict() self.ipadr = ipadr self.myport = serverPort self.idplain = str(self.ipadr) + ":" + str(self.myport) self.id = int(hashlib.sha1(self.idplain.encode('UTF-8')).hexdigest(), 16) self.first = first self.addMessage("Chord Configured to Listen to Port : " + str(serverPort) + " and FirstFlag is: " + str(self.first)) ## ChordServer part runs as a separate thread, give serverport and reference to self as callback self.chordServerThread = ChordServer(serverPort, self) self.chordServerThread.start() ## this is our dictionary which works as DB for key,value pairs self.SavedValues = {} #key=int(hashlib.sha1(name.encode('UTF-8')).hexdigest()) then Savedvalues['key'] = value if self.first == 1: ## successor and predecessor ARE ME, so in special case we populate these self.successorID = self.id self.successorIP = self.ipadr self.successorPort = self.myport self.predecessorID = self.id self.predecessorIP = self.ipadr self.predecessorPort = self.myport
class ChordController(): """ ChordController handles all Chord implementation related logic It exposes and API to the GUI application and another method for the ChordServer to call when ever messages arrive """ def __init__(self, ipadr, serverPort, first): """ Constructor ipadr: our ip address serverPort: port to bind to first: indicator if we are the first node 1=true """ self.i = 1 self.dMessages = dict() self.ipadr = ipadr self.myport = serverPort self.idplain = str(self.ipadr) + ":" + str(self.myport) self.id = int(hashlib.sha1(self.idplain.encode('UTF-8')).hexdigest(), 16) self.first = first self.addMessage("Chord Configured to Listen to Port : " + str(serverPort) + " and FirstFlag is: " + str(self.first)) ## ChordServer part runs as a separate thread, give serverport and reference to self as callback self.chordServerThread = ChordServer(serverPort, self) self.chordServerThread.start() ## this is our dictionary which works as DB for key,value pairs self.SavedValues = {} #key=int(hashlib.sha1(name.encode('UTF-8')).hexdigest()) then Savedvalues['key'] = value if self.first == 1: ## successor and predecessor ARE ME, so in special case we populate these self.successorID = self.id self.successorIP = self.ipadr self.successorPort = self.myport self.predecessorID = self.id self.predecessorIP = self.ipadr self.predecessorPort = self.myport def getNextInt(self): ## to keep track if message arrives at same millisecond self.i = self.i + 1 return self.i def addMessage(self, message): ## provides log and feedback to the GUI, everything written here ## will be passed back to any GUI for user feedback key = str(time.time()) key = key + "-" + str(self.getNextInt()) self.dMessages[key] = message def getMessages(self): return self.dMessages def control(self, urlparams): ## simple control method to wrap GUI calls, ## url parameters in urlparams based on which ## we decide on which API function to use if urlparams.get('JoinBtn', '0') == 'Join': self.join(urlparams.get('Host', "0"), urlparams.get('Port', "0")) self.addMessage(self.getStatus()) return self.dMessages if urlparams.get('StoreBtn', '0') == 'Store': self.store(urlparams.get('Name', ""), urlparams.get('Value', "")) self.addMessage(self.getStatus()) return self.dMessages if urlparams.get('LeaveBtn', '0') == 'Leave': self.leave("0") self.addMessage(self.getStatus()) return self.dMessages if urlparams.get('RetrieveBtn', '0') == 'Retrieve': self.retrieve(urlparams.get('Name' "")) self.addMessage(self.getStatus()) return self.dMessages if urlparams.get('getForm', '0') == 'true': ## no action, bt we just return what ever statuses ## have been updated as things happen even ## while GUI is not used self.addMessage(self.getStatus()) return self.dMessages def join(self, host, port): """ Join to the network host: where is a node running port: on which port it is running """ self.addMessage("Joining host: " + host + " at port: " + port) chordSocket = ChordSocket() chordSocket.connect(host, int(port)) ## construct a Join -message bSend = "JOIN " + str(self.id) + " " + self.ipadr + " " + str(self.myport) + "\r\n" self.addMessage("Sending this JOIN message to node: " + bSend) chordSocket.chordsend(bSend) retMessage = chordSocket.chordreceive() self.addMessage("Received this message as reply for JOIN: " + retMessage) if self.getCommand(retMessage) == "JOIN_OK": lrLines = retMessage.split("\r\n") ## first three values are the new predecessor npID = int(lrLines[0].split(" ")[1]) npIP = str(lrLines[0].split(" ")[2]) npPO = int(lrLines[0].split(" ")[3]) ## and the next three values are my new successor nsID = int(lrLines[0].split(" ")[4]) nsIP = str(lrLines[0].split(" ")[5]) nsPO = int(lrLines[0].split(" ")[6]) self.successorID = nsID self.successorIP = nsIP self.successorPort = nsPO self.predecessorID = npID self.predecessorIP = npIP self.predecessorPort = npPO ## need to send NEWNODE message to our predecessor chordSocketn = ChordSocket() chordSocketn.connect(self.predecessorIP, int(self.predecessorPort)) nnMessage = "NEWNODE " + str(self.id) + " " + self.ipadr + " " + str(self.myport) + "\r\n" self.addMessage("Sending this NEWNODE to predecessor: " + self.predecessorIP + ":" + str(self.predecessorPort) + " - " + nnMessage) chordSocketn.chordsend(nnMessage) chordSocketn.sock.close() chordSocket.sock.close() return 1 def leave(self, zero): """ Leave does not really take any parameters zero: can be anything, reserved for magical use """ ## so send our predecessor to our successor sLeave = "LEAVE" + " " + str(self.predecessorID) + " " + str(self.predecessorIP) + " " + str(self.predecessorPort) + "\r\n" self.addMessage(self.addMessage("Leaving, LEAVE to successor : " + str(self.successorIP) +" :" + str(self.successorPort) + " - msg: " + sLeave)) chordSocketL = ChordSocket() chordSocketL.connect(self.successorIP, int(self.successorPort)) chordSocketL.chordsend(sLeave) chordSocketL.sock.close() ## then we send transfer to successort ## TODO: the ID's from database with correct range self.transfer(self.successorIP, self.successorPort, {}) ## then we send NODEGONE to our predecessor with our successor info self.nodegone() return 1 def transfer(self, toIP, toPORT, values={}): """ Transfer dictionary full of values to destination IP and port toIP: where we transfer toPORT: which port """ chordSocketT = ChordSocket() chordSocketT.connect(toIP, int(toPORT)) ## TODO, send more than 0 items sTransfer = "TRANSFER" + " 0" "\r\n" chordSocketT.chordsend(sTransfer) chordSocketT.sock.close() return 1 def nodegone(self): """ tell our predecessor that our successor is their successor """ chordSocketG = ChordSocket() chordSocketG.connect(self.predecessorIP, self.predecessorPort) sNodegone = "NODEGONE " + str(self.successorID) + " " + str(self.successorIP) + " " + str(self.successorPort) + "\r\n" chordSocketG.chordsend(sNodegone) chordSocketG.sock.close() return 1 def store(self, name, value): """ Store figres out where to store value by key and if necessary contacts successor with STORE message """ self.addMessage("Storing: " + name + " : " + value) HashName =int(hashlib.sha1(name.encode('UTF-8')).hexdigest(), 16) if self.isItMine(self.id, self.predecessorID, HashName): self.addMessage("Storing by me, hash: " + str(HashName)) self.SavedValues[HashName] = value self.addMessage(self.SavedValues[HashName]) else: chordSocketST = ChordSocket() chordSocketST.connect(self.successorIP, self.successorPort) sStore= "STORE " + " " + str(HashName) + " : " + value + "\r\n" chordSocketST.chordsend(sStore) chordSocketST.sock.close() return 1 def retrieve(self, name): """ Retrieve value based on key, if not found locall send to successor to find it """ self.addMessage("retrieving: " + name) HashName = int(hashlib.sha1(name.encode('UTF-8')).hexdigest(), 16) if self.isItMine(self.id, self.predecessorID, HashName): self.addMessage("it is MINE in GUI: " + str(HashName)) try: value = self.SavedValues[HashName] if value: retrieveMessage = "OK "+ str(value) + "\r\n" self.addMessage(retrieveMessage) return value except: retrieveMessage = "Not Found " + "\r\n" self.addMessage(retrieveMessage) return -1 else: retrieveMessage = "Retrieve " + str(HashName) + "\r\n" chordSocketR = ChordSocket() chordSocketR.connect(self.successorID, self.successorPort) chordSocketR.chordsend(retrieveMessage) retMessage = chordSocketR.chordreceive() if self.getCommand(retMessage) == "Retrieve": self.addMessage("Message Received for Retrieve ") chordSocketR.sock.close() return 1 def getCommand(self, msg): """ Extract COMMAND part from msg """ lLines = msg.split("\r\n") return lLines[0].split(" ")[0] def handleServerMessage(self, msg, clientsocket): """ reveive message from the Chord Server Thread. msg: is a string that has \r\n separated lines clientsocket: if we need to reply something, we use that """ self.addMessage("Received Message from Server Thread with: " + msg) ## first, split the message into lines, it's CRLF separated lLines = msg.split("\r\n") ## TODO: is this info really needed? self.addMessage(lLines) ## then we determine the command from first line sCommand = lLines[0].split(" ")[0] ## TODO: do we really want to log everything in stdout print(lLines) print(sCommand) if sCommand == "NEWNODE": ## NEWNODE, we just update our successor with new info ## no reply nnID = int(lLines[0].split(" ")[1]) nnIP = str(lLines[0].split(" ")[2]) nnPO = int(lLines[0].split(" ")[3]) self.successorID = nnID self.successorIP = nnIP self.successorPort = nnPO clientsocket.close() return 1 self.addMessage(sCommand) if sCommand == "JOIN": ## we got JOIN message from Server ## we need to determine if joining us, or forward to next ## it looks lit this ## JOIN 5147385592596484592 127.0.0.1 9002 ## so below the [1] number is the field (indexed from 0) nnID = int(lLines[0].split(" ")[1]) nnIP = str(lLines[0].split(" ")[2]) nnPO = int(lLines[0].split(" ")[3]) ## so either we send back our self or ask our successor if self.isItMine(self.id, self.predecessorID, nnID): ## we have determined tat we are the point to connect to, update our own ## info and send JOIN_OK back ## JOIN_OK. This message is sent by the node which received the JOIN and ##confirms the success of JOIN to the new node. As arguments, JOIN_OK has ##the identifier, the IP address, and port of the predecessor and the ##identifier, the IP address, and port of the successor for the new node (the ##successor is the node sending JOIN_OK and the predecessor is that node ##current predecessor). JOIN_OK will be immediately followed by a ##TRANSFER@message which transfers the key@value pairs which are now ##the responsibility of the new nod retMessageS = self.getJOIN_OK_and_Update(nnID, nnIP, nnPO) retMessageS = retMessageS + "\r\n" self.addMessage(retMessageS) chordSocketS = ChordSocket(clientsocket) chordSocketS.chordsend(retMessageS) clientsocket.close() return 1 else: ## we determinet that this is not the point to JOIN, so we pass ## the message to our successor as such, wait for reply and ## pass what ever reply we got to the reply - works recursively ## from one node to another chordSocketS = ChordSocket() chordSocketS.connect(self.successorIP, int(self.successorPort)) bSendS = msg chordSocketS.chordsend(bSendS) retMessageS = chordSocketS.chordreceive() self.addMessage(retMessageS) chordSocketS = ChordSocket(clientsocket) chordSocketS.chordsend(retMessageS) clientsocket.close() return 1 elif sCommand == "JOIN_OK": return 1 ##server never get's a JOIN_OK message in this way elif sCommand == "LEAVE": ## somebody is leaving, update our PREDECESSOR from the LEAVE message self.predecessorID = int(lLines[0].split(" ")[1]) self.predecessorIP = str(lLines[0].split(" ")[2]) self.predecessorPort = int(lLines[0].split(" ")[3]) clientsocket.close() return 1 elif sCommand == "NODEGONE": ## reveived NODEGONE, update our SUCCESSOR accordingly self.successorID = int(lLines[0].split(" ")[1]) self.successorIP = str(lLines[0].split(" ")[2]) self.successorPort = int(lLines[0].split(" ")[3]) clientsocket.close() return 1 elif sCommand == "TRANSFER": ## received TRANSFER ## TODO update our dictionary self.addMessage("TRANSFER " + lLines[0]) clientsocket.close() return 1 elif sCommand == "STORE": ## received STORE message, either STORE here or pass ## to the successor HashName = int(lLines[0].split(" ")[1]) value = int(lLines[0].split(" ")[2]) if self.isItMine(self.id, self.predecessorID, HashName): self.SavedValues[HashName] = value else: chordSocketSTO = ChordSocket() chordSocketSTO.connect(self.successorIP, self.successorPort) sStore= "STORE " + " "+ name + " : " + value + "\r\n" chordSocketSTO.chordsend(sStore) chordSocketSTO.sock.close() elif sCommand == "RETRIEVE": ## received RETRIEVE, either see if should found locally and try to find ## or send to successor for search HashName = int(lLines[0].split(" ")[1]) if self.isItMine(self.id, self.predecessorID, HashName): try: value = self.SavedValues[HashName] if value: retrieveMessage = "OK "+ str(value) + "\r\n" self.addMessage(retrieveMessage) clientsocket.close() return value except: retrieveMessage = "Not Found " + "\r\n" self.addMessage(retrieveMessage) clientsocket.close() return -1 else: retrieveMessage = "Retrieve " + HashName + "\r\n" chordSocketR = ChordSocket() chordSocketR.connect(self.successorID, self.successorPort) chordSocketR.chordsend(retrieveMessage) retMessage = chordSocketR.chordreceive() if self.getCommand(retMessage) == "Retrieve": self.addMessage("Message Received for Retrieve ") chordSocketR.sock.close() return 1 def getJOIN_OK_and_Update(self, nnID, nnIP, nnPO): """ construct JOIN_OK message for passing back along the path of queries and update our status nnID: new node ID nnIP: new node IP nnPO: new node Port """ msgR = "JOIN_OK " + str(self.predecessorID) + " " + str(self.predecessorIP) + " " + str(self.predecessorPort) + " " + str(self.id) + " " + str(self.ipadr) + " " + str(self.myport) + " \r\n" self.predecessorID = int(nnID) self.predecessorIP = str(nnIP) self.predecessorPort = int(nnPO) return msgR def TransferMessage(self, nnodeId=None): ## TODO: Construct message containing the KEY, VALUES to be transferred keysValues = "" for key in self.SavedValues: if nnodeId >= key: keysValues = key+" "+self.SavedValues['key']+"\r\n" return "Responsible Key and Values "+keysandvalues def isItMine(self, myId, predecessorId, nnId): """ determines if my node is the point to connect in the ring, or if we sould pass it along very important to get this right :) myId: our own ID predecessorId: Predecessor ID nnID: new node ID """ if myId == predecessorId: ## if there is only one node return True elif nnId < myId: if predecessorId > myId : ## When I Have the smallest ID in the ring return True elif nnId > predecessorId :## When I am in the middle of the ring return True else: return False else: return False def getStatus(self): """ return status info to be used in GUI """ try: sRets = "\r\nMyID : " + str(self.id) + " Port: " + str(self.myport) + "\r\nSuccessorID : " + str(self.successorID) + " SuccessorPort: " + str(self.successorPort) + "\r\nPredecessorID: " + str(self.predecessorID) + " PredcessorPort: " + str(self.predecessorPort) + "\r\n" for k, v in sorted(self.SavedValues.items(), reverse=True): sRets = sRets + str(k) + ": " + str(v) + "\r\n" return sRets except: return "Status not known - Maybe not first or need to Join First?"