예제 #1
0
  def init (self, net=None):
    Master.init (self)
    self._data= UmbData (self._key, self._key)
    self._self= self._peers.getNavel (self._url, self._key, Normal)

    if net:
      # create a challenger for new nodes and such
      self._chal= Challenger (self)
      self._chal.start ()

      # find a ring and join it or create a new one
      self.debug (1, 'ping')
      peer= self.discover (net, self._self)
      if peer==None:
        self.debug (1, "gave up! noone's here!")
        self.createRing ()
      else:
        self.join (peer)
예제 #2
0
class Navel (Master):
  def __init__ (self, url=None, column=0, fileName=None):
    # take care of repeated keys!
    key= random (maxIno)
    Master.__init__ (self, url, key, column, fileName=fileName)
    self._succ= None
    self._pred= None

    # thread support
    # RLock's; just in case
    self._succLock= RLock ()
    self._predLock= RLock ()

    self._serverType= NavelServer
    self._chal= None

    self._self= None
    self._prevSucc= None
    self._prevPred= None

  def __str__ (self):
    return "Navel %s" % self.url ()

  def init (self, net=None):
    Master.init (self)
    self._data= UmbData (self._key, self._key)
    self._self= self._peers.getNavel (self._url, self._key, Normal)

    if net:
      # create a challenger for new nodes and such
      self._chal= Challenger (self)
      self._chal.start ()

      # find a ring and join it or create a new one
      self.debug (1, 'ping')
      peer= self.discover (net, self._self)
      if peer==None:
        self.debug (1, "gave up! noone's here!")
        self.createRing ()
      else:
        self.join (peer)

  def amAuth (self, key):
    # ! someone should have the lock on succ
    m= self._key
    n= self.getSucc ().key ()
    if coBetween (m, key, n):
      return True
    else:
      return False

  def params (self):
    return (self._url, self._key)

  def getPred (self):
    pred= self._pred
    return pred

  def setPred (self, pred):
    self._predLock.acquire ()
    self.debug (1, "changing pred to %s" % pred)
    if self._pred:
      self._pred.setKind (Normal)
    self._pred= pred
    if self._pred:
      self._pred.setKind (Pred)
    self._predLock.release ()

  def getSucc (self):
    succ= self._succ
    return succ

  def setSucc (self, succ):
    self._succLock.acquire ()
    self.debug (1, "changing succ to %s" % succ)
    if self._succ:
      self._succ.setKind (Normal)
    self._succ= succ
    if self._succ:
      self._succ.setKind (Succ)
      self._data.resizeFree (succ.key ())
    self._succLock.release ()


  ###########
  # chord impl
  ###########
  def createRing (self, n=0):
    """
      creates a umb ring, setting our succ to ourself
      returns: naught
    """
    # the str is for the URL parser
    self.debug (1, 'creating ring')
    self.setSucc (self._peers.getNavel (self._url, self._key, Succ))
    self.addRandomData (n)
    self.debug (1, 'ring created')

  def findSucc (self, key):
    """
      simple key location
      returns: the peer
    """
    succ= self.getSucc ()
    if ocBetween (self.key (), key, succ.key ()):
      return succ
    else:
      # ask the next one
      self.debug (1, "asking %s about succ of %d" % (succ, key))
      (url, key)= succ.findSucc (key)
      # succ.close ()
      # add it to the known peer's list
      return self._peers.getNavel (url, key)

  def fastFindSucc (self, key):
    """
      scalable key location
      returns: the peer
    """
    succ= self.getSucc ()
    self.debug (1, "being asked about succ of %d" % (key))
    if succ and ocBetween (self.key (), key, succ.key ()):
      return succ
    else:
      try:
        # ask the closest one
        # WRONG: must find the closest one not connected (or something)
        # we can try to defer the realization of this new peer.
        # peer= self.findAuth (key)
        peer= self.findKnownSucc (key)
        if not peer==self._self:
          self.debug (1, "asking %s about succ of %d" % (peer, key))
          (url, key)= peer.findSucc (key)
          if peer.kind ()==Normal:
            peer.close ()
        else:
          self.debug (1, 'fallbacking to myself')
          (url, key)= self.params ()
          url= str(url)
        # add it to the known peer's list
        return self._peers.getNavel (url, key)
      except UmbDead:
        # that guy's dead! let's bury him!
        self._peers.delNavel (peer.key ())
        # and try again
        return self.fastFindSucc (key)

  def join (self, other):
    self.debug (1, "joining to %s" % other)
    # WTF?
    # self.pred= None
    self._pred= None

    self._prevSucc= self.getSucc ()
    try:
      (url, key)= other.findSucc (self.key ())
      peer= self._peers.getNavel (url, key)
      self.setSucc (peer)
      self.debug (1, "succ found %s, %s" % (url, key))
      self.debug (1, 'notifying w/ %s' % peer)
      if peer.notify (self.params ()):
        self.keypass ()
    except UmbDead:
      # that one's dead, rollback
      self.setSucc (self._prevSucc)
      self._peers.delNavel (key)
    self.debug (1, 'join finished')

  def keypass (self):
    succ= self.getSucc ()
    if not succ==self._self:
      # here we also pass to the new node its data
      peer= succ
      self.debug (1, 'got succ, giving data')
      given= self.giveData (self.key (), peer.key (), self.Complement)
      ans= peer.takeData (given)
      # iterate as the keys get rejected 'cause there's another auth
      ok= ans[0]
      while not ok:
        (url, key)= ans[1]
        self.debug (1, "redirect to: %s:%d" % (url, key))
        peer= self._peers.getNavel (url, key)
        if not peer==self._self:
          ans= peer.takeData (self.giveData (self.key (), peer.key (), self.Complement))
          ok= ans[0]

      # see keypass.txt
      pred= self.getPred ()
      if not pred:
        self.debug (1, 'no pred, succ.tellPredToForget ()')
        succ.tellPredToForget ()
      else:
        # try not to do stupid things
        # (i.e., don't tell ourselves to forget nor our new succ)
        # if not pred==self._self and not pred==succ:
        if not pred==succ:
          self.debug (1, '%s.forgetData (given.keys ())' % pred)
          # peer= pred
          pred.forgetData (given.keys ())
        else:
          self.debug (1, 'I wouldn\'t guess that this would happen. pred is: %s' % pred)
    if self._prevSucc:
      # close connection to the 'old' succ
      self._prevSucc.close ()

  def stabilize (self):
    """
      implements stabilize code of Chord's paper, fig 6
      with some error handling
      returns: naught
    """
    self.debug (1, "stab!")
    # * we must have a head to talk to
    succ= self.getSucc ()
    if succ:
      self._succLock.acquire ()
      self.debug (2, "stab-2!")
      try:
        (url, key)= succ.pred ()
        self.debug (1, "stab-3! key: %s" % str(key))
        # None,None means he has none
        if key and ooBetween (self.key (), key, succ.key ()):
          # save it for restoring on exception
          self._prevSucc= succ
          succ= self._peers.getNavel (url, key)
          self.debug (1, "stab-4!")
          self.setSucc (succ)
          self.debug (1, "stab-5!")
          try:
            # do not give data to ourselves
            self.debug (1, 'notifying w/ %s' % succ)
            if succ.notify (self.params ()):
              self.keypass ()
          except UmbDead:
            self.debug (1, "%s:%s's dead, Jim!" % (url, key))
            # self.delNavel (key)
            # this one's dead; restore the previous one
            self.setSucc (self._prevSucc)
            # should we notify the old one?
        else:
          # don't care about this
          self.debug (1, 'notifying w/ %s' % succ)
          succ.notify (self.params ())

      except UmbDead:
        self.debug (1, "%s's dead, Jim!" % (self._succ.key ()))
        # self.delNavel (self._succ.key ())
        self.setSucc (None)

      self._succLock.release ()
      self.debug (1, 'stab finished')

  def notify (self, url, key):
    """
      implements chord's notify.
      returns: True if the notifying peer is now our pred, False otherwise
    """
    result= False
    self.debug (1, "notify!")
    self._predLock.acquire ()
    self.debug (2, "pred locked")
    pred= self.getPred ()
    prevPred= pred

    if not pred or ooBetween (pred.key (), key, self.key ()):
      # self.debug (1, "set %s,%s as new pred" % (url, key))
      pred= self._peers.getNavel (url, key)
      self.setPred (pred)

      # don't talk in the brain
      if not pred==self._self:
        # give our data so he makes a backup
        self.debug (1, 'giving data as backup')
        # this may fail
        try:
          given= self.giveData (self.key (), self._succ.key (), self.Set)
          pred.backupData (given)
          # shouldn't we also tell him to forget about anything else?
          # nope
          if prevPred:
            # no more business w/ him
            prevPred.close ()
          # gotta save it for later ref.
          self._prevPred= prevPred
        except UmbDead:
          self.debug (1, '\'tis dead, Jim!')
          self.setPred (prevPred)
      result= True

    self._predLock.release ()
    self.debug (2, "pred released")
    return result

  def checkPred (self):
    self._predLock.acquire ()
    if self._pred.isDead ():
      self._pred= None
    self._predLock.release ()


  ############
  # extensions
  ############
  def findAuth (self, key):
    """
      looks for the auth of the key, but only in the already know peers.
      returns: the peer
    """
    self.debug (1, "findAuth! %d" % key)
    # we already have ourselves in the peer list, so there's at least one answer.
    peer= self._peers.getAuthNavel (key)
    return peer

  def findKnownSucc (self, key):
    """
      looks for the succ of the key, but only in the already know peers.
      returns: the peer
    """
    self.debug (1, "findKnownSucc! %d" % key)
    # * then 'and ...' is when have head but no tail
    if self._succ and self._pred:
      peer= self._peers.getSuccNavel (key)
      return peer
    else:
      self.debug (1, "fall back!")
      # is *this* right?
      return self._peers.getAuthNavel (self._key)

  def joinTo (self, url, key=None):
    """
      joins to a certain peer. asks the peer's key for adding it to the list and
      checks out that our key is not already in the ring (not implemented yet).
      returns: naught
    """
    if not key:
      # ask for the key...
      key= self.getNavelKey (url)
    # ... so we can add it to the server list properly.
    peer= self._peers.getNavel (url, key)
    terminated= self.join (peer)
    # while not terminated:
      # can fail only when the key already exists
      # TODO
      # terminated= self.join (peer)

  def processKeyedData (self, data, function, backupFunction=None):
    keys= data.keys ()
    keysQty= len (keys)
    accepted= {}
    i= 0
    added= 0
    m= self.key ()
    n= self.getSucc ().key ()
    wrongKey= None
    while i<keysQty:
      key= keys[i]
      args= data[key]
      if coBetween (m, key, n):
        self.debug (2, '[%d,%d] processing %d; data %s' % (i, keysQty, key, args))
        function (key, args)
        if backupFunction:
          accepted[key]= args
        added+= 1
      else:
        self.debug (2, '[%d,%d] dropping %d' % (i, keysQty, key))
        if not wrongKey:
          wrongKey= key
      i+= 1
    if backupFunction and len (accepted.keys())>0:
      backupFunction (accepted)
    if added<keysQty:
      # some values don't belong to us
      server= self.findAuth (wrongKey)
      return server
    else:
      return None

  def keepInSync (self, hash):
    # keep backup n'sync
    pred= self.getPred ()
    if pred:
      pred.backupData (hash)

  def addValues (self, key, values):
    """
      adds the values to the key
      returns: naught
    """
    self._data.addValues (key, values)

  def setValues (self, key, values):
    """
      sets the new value at key, forgetting whatever was there.
      use with caution!
      returns: naught
    """
    self._data.setValues (key, values)
    # keep backup n'sync
    # no, this is the one used for backup and taking itself

  def delValues (self, key, values):
    """
      deletes the value from the key's associated list
      returns: naught
    """
    self._data.delValues (key, values)

  def delKey (self, key, none):
    """
      deletes the key at all
      returns: naught
    """
    self._data.delKey (key)

  def forgetData (self, keys):
    self._data.acquire ()
    for key in keys:
      # only those that do not belong to us.
      if not self.amAuth (key):
        self._data.delKey (key)
    self._data.release ()

  def addRandomData (self, n):
    i= 0
    while i<n:
      key= random (1000)
      char= chr (ord ('a')+random (3))
      self._data.addValue (key, char)
      i+= 1

  def tellPredToForget (self):
    prevPred= self._prevPred
    if prevPred and not prevPred==self._self:
      # only if it's not us
      self.tellPeerToForget (prevPred, self.key (), self._succ.key (), self.Set)
      # no more no more
      # as in bye bye
      self._prevPred= None

  def tellPeerToForget (self, peer, m, n, what):
    self.debug (1, '%s.forgetData (given.keys ())' % peer)

    given= self.giveData (m, n, what)
    keys= given.keys ()
    keys.sort ()
    keys.reverse ()

    ans= peer.forgetData (keys)
    ok= ans[0]
    while not ok:
      (url, key)= ans[1]
      self.debug (1, "redirect to: %s:%d" % (url, key))
      peer= self._peers.getNavel (url, key)
      if not peer==self._self:
        # WTF?!?
        ans= peer.takeData (self.giveData (self.key (), peer.key (), what))
        ok= ans[0]

  def emp (self):
    self._data.emp ()

  def ident (self):
    params= self.params ()
    # ugly as old man butts
    return ["i am", str(params[0]), params[1]]

  def periodic (self):
    self.debug (1, 'periodic')
    Master.periodic (self)
    if not self._terminate:
      # we migh been asked to finish right now.
      self.stabilize ()

      if random (10)==1:
        peer= self._peers.getRandomNavel ()
        # don't gossip w/ myself!
        if not peer==self._self:
          self.gossip (peer)

      self.debug (1, "%s <-- %d --> %s" % (self._pred and self._pred.key () or None, self.key (), self._succ and self._succ.key () or None))
    else:
      self.debug (1, 'you might be pregnant...')

  def stop (self):
    self.debug (1, "join'ing challenger...")
    self._chal.join ()
    Master.stop (self)