Ejemplo n.º 1
0
    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            
Ejemplo n.º 2
0
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?"