1 ## Copyright 2002-2003 Andrew Loewenstern, All Rights Reserved
2 # see LICENSE.txt for license information
4 """Represents a node in the DHT.
6 @type NULL_ID: C{string}
7 @var NULL_ID: the node ID to use until one is known
10 from datetime import datetime, MINYEAR
11 from types import InstanceType
13 from twisted.trial import unittest
16 from util import compact
18 # magic id to use before we know a peer's id
22 """Encapsulate a node's contact info.
25 @ivar fails: number of times this node has failed in a row
26 @type lastSeen: C{datetime.datetime}
27 @ivar lastSeen: the last time a response was received from this node
29 @ivar id: the node's ID in the DHT
31 @ivar num: the node's ID in number form
33 @ivar host: the IP address of the node
35 @ivar port: the port of the node
36 @type token: C{string}
37 @ivar token: the last received token from the node
38 @type num_values: C{int}
39 @ivar num_values: the number of values the node has for the key in the
40 currently executing action
43 def __init__(self, id, host = None, port = None):
44 """Initialize the node.
46 @type id: C{string} or C{dictionary}
47 @param id: the node's ID in the DHT, or a dictionary containing the
48 node's id, host and port
50 @param host: the IP address of the node
51 (optional, but must be specified if id is not a dictionary)
53 @param port: the port of the node
54 (optional, but must be specified if id is not a dictionary)
57 self.lastSeen = datetime(MINYEAR, 1, 1)
59 # Alternate method, init Node from dictionary
60 if isinstance(id, dict):
65 assert isinstance(id, str)
66 assert isinstance(host, str)
68 self.num = khash.intify(id)
73 self._contactInfo = None
75 def updateLastSeen(self):
76 """Updates the last contact time of the node and resets the number of failures."""
77 self.lastSeen = datetime.now()
80 def updateToken(self, token):
81 """Update the token for the node."""
84 def updateNumValues(self, num_values):
85 """Update how many values the node has in the current search for a value."""
86 self.num_values = num_values
89 """Log a failed attempt to contact this node.
92 @return: the number of consecutive failures this node has
94 self.fails = self.fails + 1
97 def contactInfo(self):
98 """Get the compact contact info for the node."""
99 if self._contactInfo is None:
100 self._contactInfo = compact(self.id, self.host, self.port)
101 return self._contactInfo
104 return `(self.id, self.host, self.port)`
106 #{ Comparators to bisect/index a list of nodes with either a node or a long
108 if type(a) == InstanceType:
112 if type(a) == InstanceType:
116 if type(a) == InstanceType:
120 if type(a) == InstanceType:
124 if type(a) == InstanceType:
128 if type(a) == InstanceType:
133 class TestNode(unittest.TestCase):
134 """Unit tests for the node implementation."""
136 self.node = Node(khash.newID(), '127.0.0.1', 2002)
137 def testUpdateLastSeen(self):
138 t = self.node.lastSeen
139 self.node.updateLastSeen()
140 self.failUnless(t < self.node.lastSeen)