~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to sandbox/chord.py

  • Committer: Bazaar Package Importer
  • Author(s): Moshe Zadka
  • Date: 2002-03-08 07:14:16 UTC
  • Revision ID: james.westby@ubuntu.com-20020308071416-oxvuw76tpcpi5v1q
Tags: upstream-0.15.5
ImportĀ upstreamĀ versionĀ 0.15.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Twisted, the Framework of Your Internet
 
2
# Copyright (C) 2001 Matthew W. Lefkowitz
 
3
 
4
# This library is free software; you can redistribute it and/or
 
5
# modify it under the terms of version 2.1 of the GNU Lesser General Public
 
6
# License as published by the Free Software Foundation.
 
7
 
8
# This library is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
11
# Lesser General Public License for more details.
 
12
 
13
# You should have received a copy of the GNU Lesser General Public
 
14
# License along with this library; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
 
18
# TODO:
 
19
# failure detection
 
20
# voluntary delinking
 
21
# successor list
 
22
# HyperChord
 
23
 
 
24
import random
 
25
import sha
 
26
import socket
 
27
import struct
 
28
from twisted.spread import pb
 
29
 
 
30
NBIT = 160    # Size (in bits) of Chord identifiers
 
31
vNodes = 1    # sure hope you dont want more than 256.        
 
32
 
 
33
def between(n, a, b):
 
34
    """Check if n in interval(a, b) on the circle."""
 
35
    if   a == b: return n != a                  # n is the only node not in (n, n)
 
36
    elif a < b: return n > a and n < b
 
37
    else: return n > a or n < b
 
38
 
 
39
def betweenL(n, a, b):
 
40
    """Check if n in interval[a, b) on the circle."""
 
41
    if a == b and n == a: return 1
 
42
    elif a < b: return n >= a and n < b
 
43
    else: return n >= a or n < b
 
44
 
 
45
def betweenR(n, a, b):
 
46
    """Check if n in interval(a, b] on the circle."""
 
47
    if a == b and n == a: return 1
 
48
    elif a < b: return n > a and n <= b
 
49
    else: return n > a or n <= b
 
50
 
 
51
 
 
52
class Node(pb.Copyable, pb.Perspective):
 
53
    """A node in a Chord network."""
 
54
    
 
55
    def __init__(self, address, perspectiveName, identityName, port=pb.portno):
 
56
        global vNodes
 
57
        pb.Perspective.__init__(self, perspectiveName, identityName) 
 
58
        self.id = sha.new(socket.inet_aton(address) + struct.pack('!h', port) + chr(vNodes)).digest()
 
59
        self.address = (address, port, vNodes)
 
60
        vNodes = vNodes + 1
 
61
        self.finger = [None] * (NBIT+1)
 
62
        self.lastFixed = 1  # Needed for fixFingers
 
63
        self.pred = None
 
64
    
 
65
    def getStateToCopyFor(self, persp):
 
66
            return {"id": self.id, "address": self.address}
 
67
 
 
68
    def __repr__(self):
 
69
        return "<Node " + `self.id` + ">"
 
70
    
 
71
    def join(self, n2):
 
72
        """Intialize finger tables of local node.
 
73
        n2 is a node already on the network or None if we're the first."""
 
74
        if n2:
 
75
            n2.findSuccessor(self.id).addCallback(lambda x, self=self: self.getNodeAt(x).addCallback(self._setFingerCallback, callbackArgs=(1,)))
 
76
            self.finger[1].notify(self.address, pbanswer=0)
 
77
            self.findPredecessor(self.id).addCallback(self._setPredCallback)
 
78
            for i in range(1, NBIT):
 
79
                if betweenL(self.id, self.start(i+1), 
 
80
                  self.finger[i].id):
 
81
                    self.finger[i+1] = self.finger[i]
 
82
                else:
 
83
                    n2.findSuccessor(self.start(i+1)).addCallback(lambda x, self=self, i=i: self.getNodeAt(x).addCallback(self._setFingerCallback, callbackArgs=(i+1,)))
 
84
            n2.notify(self.address, pbanswer=0)
 
85
        else:
 
86
            self.finger[1] = None
 
87
            self.pred = None
 
88
 
 
89
    def _setFingerCallback(self, x, i):
 
90
        #CRUM! DEFEATED BY DISTANCE
 
91
        self.finger[i] = x
 
92
 
 
93
    def _setPredCallback(self, n):
 
94
        #ALSO TOO FAR!
 
95
        self.pred = n
 
96
 
 
97
    def perspective_getSuccessor(self):
 
98
        return self.finger[1]
 
99
    
 
100
    def perspective_getPredecessor(self):
 
101
        return self.pred
 
102
    
 
103
    def perspective_findSuccessor(self, id):
 
104
        self.findPredecessor(id).addCallback(self.findSuccessor_1)
 
105
 
 
106
    def findSuccessor_1(self, n2):
 
107
        n2.getSuccessor().addCallbacks(self.findSuccessor_2, callbackArgs=n2)
 
108
        
 
109
    def findSuccessor_2(self, address, n2):
 
110
        if address: 
 
111
            return address
 
112
        else:
 
113
            return n2.address
 
114
    
 
115
    def findPredecessor(self, id):
 
116
        if self.finger[1] is None:
 
117
            return self
 
118
        n2 = self
 
119
        return self.findPredecessor_1(n2, id)        
 
120
    
 
121
    def findPredecessor_1(self, n2, id):
 
122
        n2.getSuccessor().addCallback(self.findPredecessor_2, n2, id)
 
123
    
 
124
    def findPredecessor_2(self, n3, n2, id):
 
125
        if not betweenR(id, n2.id, n3.id):
 
126
            n2.closestPrecedingFinger(id).addCallback(lambda a,  id=id, self=self: self.getNodeAt(a).addCallback(lambda n, id=id, self=self: self.findPredecessor_1(n, id)))
 
127
        else:
 
128
            return n2
 
129
 
 
130
    def perspective_closestPrecedingFinger(self, id):
 
131
        for i in xrange(NBIT, 0, -1):
 
132
            if not self.finger[i]: continue
 
133
            if between(self.finger[i].id, self.id, id):
 
134
                return self.finger[i].address
 
135
        return self.address
 
136
 
 
137
    def getNodeAt(self, address):
 
138
        return pb.connect(address[0], address[1], "chord", "", "chord").addCallback(lambda n: n.getSelf())
 
139
        
 
140
    def getSelf(self):
 
141
        # necessary to produce proper Copyable behaviour
 
142
        return self
 
143
    
 
144
    def stabilise(self):
 
145
        """Verify our immediate successor and tell them about us.
 
146
        Called periodically."""
 
147
        self.finger[1].getPredecessor().addCallback(self.stabilise_1)
 
148
 
 
149
    def stabilise_1(self, p):
 
150
        if p != self.address:
 
151
            self.perspective_notify(p)
 
152
 
 
153
    def fixFingers(self):
 
154
        """Refresh a random finger table entry.
 
155
        Called periodically."""
 
156
        i = random.randrange(1, len(self.finger)+1)
 
157
        self.finger[i] = self.findSuccessor(self.start(i))
 
158
    
 
159
    def perspective_notify(self, addr):        
 
160
        """n thinks it might be our predecessor."""
 
161
        self.getNodeAt(addr).addCallback(self.notify)
 
162
 
 
163
    def notify(self, n):   
 
164
        if (self.pred is self or self.pred is None or 
 
165
          between(n.id, self.pred.id, self.id)):
 
166
            self.pred = n
 
167
            n.notify(self.address, pbanswer=0)
 
168
        if self.finger[1] is None or between(n.id, self.id, self.finger[1].id):
 
169
            self.finger[1] = n
 
170
            n.notify(self.address)
 
171
        for i in xrange(2, len(self.finger)):
 
172
            if self.finger[i] is None or betweenL(n.id, self.start(i), self.finger[i].id):
 
173
                self.finger[i] = n
 
174
 
 
175
    def start(self, k):
 
176
        assert 1 <= k <= NBIT
 
177
        r = (self.id + 2L**(k-1)) % 2L**NBIT
 
178
        if r == 0:
 
179
            return 2L**NBIT
 
180
        else:
 
181
            return r
 
182
 
 
183
class ChordService(pb.Service):
 
184
    def __init__(self, address, port, serviceName, application=None):
 
185
        pb.Service.__init__(self, serviceName, application)
 
186
        self.address = address
 
187
        self.portno = port
 
188
        
 
189
    def createPerspective(self, name):
 
190
        p = Node(self.address, name, name, self.portno)
 
191
        self.perspectives[name] = p
 
192
        p.setService(self)
 
193
        return p