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.
24 @ivar conn: the connection to the remote node (added externally)
25 @ivar table: the routing table (added externally)
27 @ivar fails: number of times this node has failed in a row
28 @type lastSeen: C{datetime.datetime}
29 @ivar lastSeen: the last time a response was received from this node
31 @ivar id: the node's ID in the DHT
33 @ivar num: the node's ID in number form
35 @ivar host: the IP address of the node
37 @ivar port: the port of the node
38 @type token: C{string}
39 @ivar token: the last received token from the node
40 @type num_values: C{int}
41 @ivar num_values: the number of values the node has for the key in the
42 currently executing action
45 def __init__(self, id, host = None, port = None):
46 """Initialize the node.
48 @type id: C{string} or C{dictionary}
49 @param id: the node's ID in the DHT, or a dictionary containing the
50 node's id, host and port
52 @param host: the IP address of the node
53 (optional, but must be specified if id is not a dictionary)
55 @param port: the port of the node
56 (optional, but must be specified if id is not a dictionary)
59 self.lastSeen = datetime(MINYEAR, 1, 1)
61 # Alternate method, init Node from dictionary
62 if isinstance(id, dict):
67 assert isinstance(id, str)
68 assert isinstance(host, str)
70 self.num = khash.intify(id)
75 self._contactInfo = None
77 def updateLastSeen(self):
78 """Updates the last contact time of the node and resets the number of failures."""
79 self.lastSeen = datetime.now()
82 def updateToken(self, token):
83 """Update the token for the node."""
86 def updateNumValues(self, num_values):
87 """Update how many values the node has in the current search for a value."""
88 self.num_values = num_values
91 """Log a failed attempt to contact this node.
94 @return: the number of consecutive failures this node has
96 self.fails = self.fails + 1
99 def contactInfo(self):
100 """Get the compact contact info for the node."""
101 if self._contactInfo is None:
102 self._contactInfo = compact(self.id, self.host, self.port)
103 return self._contactInfo
106 return `(self.id, self.host, self.port)`
108 #{ Comparators to bisect/index a list of nodes with either a node or a long
110 if type(a) == InstanceType:
114 if type(a) == InstanceType:
118 if type(a) == InstanceType:
122 if type(a) == InstanceType:
126 if type(a) == InstanceType:
130 if type(a) == InstanceType:
135 class TestNode(unittest.TestCase):
136 """Unit tests for the node implementation."""
138 self.node = Node(khash.newID(), '127.0.0.1', 2002)
139 def testUpdateLastSeen(self):
140 t = self.node.lastSeen
141 self.node.updateLastSeen()
142 self.failUnless(t < self.node.lastSeen)